Я пишу функцию в своем проекте 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;
}
Также entire_file += buffer; выглядит проблематично. Вы должны убедиться, что то, что читается из буфера, завершается нулем (например, используйте memset() перед чтением).
Нет.
memset не будет делать джек, если чтение полностью заполнит буфер. Вам также придется зарезервировать дополнительный байт для терминатора, иначе ошибка будет бах.
@ user4581301 Хороший вопрос.
Цикл кажется бессмысленным. Прочитайте весь файл за один вызов ReadFile.
Кстати, 2048 лучше, чем 2000. Кроме того, на некоторых (всех?) компиляторах вы предотвращаете RVO с двумя разными операторами возврата, вы можете переместить объявление переменной entire_file перед первым if и сделать return entire_file вместо return ""
Спасибо за советы. Я новичок в C++ и ценю помощь.





entire_file += buffer;
предполагает, что буфер представляет собой строку с нулевым завершением, что в вашем случае не так.
Попробуй это
entire_file.append(buffer, dwBytesRead);
Это хороший пример кода, который должен был насторожить, потому что вы не использовали переменную dwBytesRead (кроме завершения цикла).
Спасибо, я так и подумал, наверное. Инициализация буфера с помощью calloc и чтение в цикле на один байт меньше его размера устранила проблему.
Неа. Это не решает проблему. Таким образом вы не сможете правильно обработать последний блок текста. Опять же, я не вижу смысла в цикле.
@DavidHeffernan Как так? Код выглядит нормально для меня. Также не смотрите, как обрабатывать без цикла, если вы заранее не знаете размер файла. Если у вас есть улучшения или альтернативы, было бы неплохо увидеть реальный код.
Ваш код в порядке. То, что Лиз описала в комментарии выше, не является таковым. Окончательная конкатенация не будет заканчиваться нулем. «Если вы заранее не знаете размер файла». Будем, это известно. Поэтому выделите строку этой длины и прочитайте ее в буфер.
Почему вы используете собственные функции winapi для чтения файла? Что не так с
std::ifstream?