WinAPI ReadFile возвращает поврежденные данные

Я пишу функцию в своем проекте Visual C++, которая считывает содержимое файла через WinAPI с шагом 2000 байт и возвращает его как std::string.

Проблема возникает, когда файл намного больше, чем буфер (например, 100 КБ), я получаю мусор, добавленный в нескольких местах в файле в середине действительных данных. Это длинная последовательность 0xcccccccc..., заканчивающаяся 3-4 другими байтами, обычно появляющаяся в середине слова. В противном случае функция не завершается ошибкой, и ни один из допустимых данных не отсутствует.

Я не проверял точные позиции, но кажется, что это происходит при увеличении размера буфера (или множителе приращений размера буфера). Если я увеличу размер буфера больше, чем размер тестовых файлов, проблема исчезнет. Из-за чего это происходит? Что я делаю неправильно?

std::string read_file(std::string filename) {
    HANDLE hFile = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        std::string errortext("Error opening " + filename + ", bad handle value: " + to_string((int)hFile));
        MessageBox(hwnd, errortext.c_str(), "Error", 0);
        return "";
    }
    char buffer[2000] = "";
    std::string entire_file = "";
    DWORD dwBytesRead = 0;

    while (ReadFile(hFile, buffer, sizeof(buffer), &dwBytesRead, NULL))
    {
        if (!dwBytesRead)
            break;
        entire_file += buffer;
    }
    CloseHandle(hFile);
    return entire_file;
}

Почему вы используете собственные функции winapi для чтения файла? Что не так с std::ifstream?

πάντα ῥεῖ 30.06.2019 08:18

Также entire_file += buffer; выглядит проблематично. Вы должны убедиться, что то, что читается из буфера, завершается нулем (например, используйте memset() перед чтением).

πάντα ῥεῖ 30.06.2019 08:20

Нет.

Martin James 30.06.2019 08:26
memset не будет делать джек, если чтение полностью заполнит буфер. Вам также придется зарезервировать дополнительный байт для терминатора, иначе ошибка будет бах.
user4581301 30.06.2019 08:28

@ user4581301 Хороший вопрос.

πάντα ῥεῖ 30.06.2019 08:31

Цикл кажется бессмысленным. Прочитайте весь файл за один вызов ReadFile.

David Heffernan 30.06.2019 09:24

Кстати, 2048 лучше, чем 2000. Кроме того, на некоторых (всех?) компиляторах вы предотвращаете RVO с двумя разными операторами возврата, вы можете переместить объявление переменной entire_file перед первым if и сделать return entire_file вместо return ""

Mirko 30.06.2019 12:23

Спасибо за советы. Я новичок в C++ и ценю помощь.

Liz 01.07.2019 22:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
9
392
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
entire_file += buffer;

предполагает, что буфер представляет собой строку с нулевым завершением, что в вашем случае не так.

Попробуй это

entire_file.append(buffer, dwBytesRead);

Это хороший пример кода, который должен был насторожить, потому что вы не использовали переменную dwBytesRead (кроме завершения цикла).

Спасибо, я так и подумал, наверное. Инициализация буфера с помощью calloc и чтение в цикле на один байт меньше его размера устранила проблему.

Liz 01.07.2019 22:27

Неа. Это не решает проблему. Таким образом вы не сможете правильно обработать последний блок текста. Опять же, я не вижу смысла в цикле.

David Heffernan 01.07.2019 23:37

@DavidHeffernan Как так? Код выглядит нормально для меня. Также не смотрите, как обрабатывать без цикла, если вы заранее не знаете размер файла. Если у вас есть улучшения или альтернативы, было бы неплохо увидеть реальный код.

john 02.07.2019 08:30

Ваш код в порядке. То, что Лиз описала в комментарии выше, не является таковым. Окончательная конкатенация не будет заканчиваться нулем. «Если вы заранее не знаете размер файла». Будем, это известно. Поэтому выделите строку этой длины и прочитайте ее в буфер.

David Heffernan 02.07.2019 08:39

Другие вопросы по теме