2009-08-19 1 views
2

Чтобы сделать двоичный сравнитель, я пытаюсь прочитать в двоичном содержимом двух файлов с помощью функции CreateFileW. Тем не менее, это приводит к тому, что весь файл буферизуется в память, и это становится проблемой для больших (500 МБ) файлов.Чтение двоичных файлов без буферизации всего файла в памяти в C++

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

Пока что лучший совпадение, похоже, я нашел ReadFile. Кажется, у него есть определенный буфер, но я не совсем уверен, что не будет другого буфера, реализованного за кулисами, например, с CreateFileW.

У вас есть какие-либо данные о том, что было бы хорошей функцией для использования?

+3

Почему вы считаете, что 'CreateFile' перезагружает весь файл в память? –

+1

@pavel имеет хорошую точку. CreateFileW не сохраняет весь файл в памяти. Это в значительной степени просто создает дескриптор файла. Вы можете использовать дескриптор для чтения всего файла или частей файла - по вашему выбору. – leeeroy

+0

Вы правы, я только что понял, что код, который я смотрел, вручную хранил все в памяти, отсюда и гигантский след. Так что это то, что вам нравится иметь дело с устаревшим кодом, grr ... –

ответ

7

Для этого вы можете использовать файлы с отображением памяти. открыть с createFile, используйте createFileMapping, затем MapViewOfFile, чтобы получить указатель на данные.

+0

Это именно то, что мне нужно, спасибо! –

5

Не уверен, что вы подразумеваете под буферизацией CreateFile - CreateFile не будет считываться во всем содержимом файла, и, кроме того, вам нужно вызвать CreateFile, прежде чем вы сможете позвонить в ReadFile.

ReadFile будет делать то, что вы хотите - операционная система может сделать некоторые упреждающее чтение данных для opportunisticly кэша данных, но он не будет читать все 500 Мб файла в

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

Как уже упоминалось, вы также можете использовать файлы с отображением памяти. Разница между файлами с памятью и ReadFile - это в основном только интерфейс - в конечном итоге файловый менеджер будет удовлетворять запросы аналогичным образом, включая некоторую буферизацию. Интерфейс выглядит немного более интуитивно понятным, но имейте в виду, что любые возникающие ошибки приведут к исключению, которое нужно будет поймать, иначе оно приведет к сбою вашей программы.

+0

Он может беспокоиться о виртуальной памяти - в 32-разрядном адресном пространстве может быть недостаточно места для его 500 МБ файлов. Вопрос о том, действительно ли он скопирован в ОЗУ, не будет иметь отношения к делу. –

+0

Правильно, но вам не нужно читать 500 МБ за раз. – Michael

5

Вызов CreateFile() сам не хранит или не считывает содержимое целевого файла. После вызова CreateFile(), вы должны вызвать ReadFile(), чтобы получить остальные части файла, который вы хотите, например, чтобы прочитать первый килобайт файла:

DWORD cbRead; 
BYTE buffer[1024]; 
HANDLE hFile = ::CreateFile(filename, 
          GENERIC_READ, 
          FILE_SHARE_READ, 
          NULL, 
          OPEN_EXISTING, 
          FILE_ATTRIBUTE_NORMAL, 
          NULL); 
::ReadFile(hFile, sizeof(buffer), &cbRead, NULL); 
::CloseHandle(hFile); 

Кроме того, если вы хотите, чтобы прочитать случайную часть файл, вы можете использовать SetFilePointer() перед вызовом ReadFile(), например, чтобы прочитать один килобайт начиная одного мегабайта в файл:

DWORD cbRead; 
BYTE buffer[1024]; 
HANDLE hFile = ::CreateFile(filename, 
          GENERIC_READ, 
          FILE_SHARE_READ, 
          NULL, 
          OPEN_EXISTING, 
          FILE_ATTRIBUTE_NORMAL, 
          NULL); 
::SetFilePointer(hFile, 1024 * 1024, NULL, FILE_BEGIN); 
::ReadFile(hFile, sizeof(buffer), &cbRead, NULL); 
::CloseHandle(hFile); 

вы можете, конечно, назвать SetFilePointer() и ReadFile() столько раз, как вы хотите, пока файл открыт. Вызов ReadFile() неявно устанавливает указатель файла на байт сразу после последнего байта, прочитанного ReadFile().

Кроме того, вы должны прочитать документацию для используемого вами File Management Functions и проверить возвращаемые значения соответствующим образом, чтобы уловить любые ошибки, которые могут возникнуть.

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

Смежные вопросы