Как работает чтение больших файлов в интерфейсе для чтения файлов
Что проверяют
Умеете ли вы спроектировать интерфейс чтения так, чтобы файл на десятки гигабайт не грузился в память целиком, и знаете ли, чем реально ускоряют чтение в C++. Следом спрашивают про отображение файла в память и про то, как тестировать код, который читает файлы.
Как отвечать
Начните с интерфейса. Он отдаёт данные порциями, а не возвращает всё содержимое:
- абстракция: класс
FileReaderс виртуальным деструктором и методом вродеstd::size_t read(std::span<std::byte> buf), который возвращает число прочитанных байт и0на конце файла. Альтернатива: обход через колбэк или итератор по строкам; - владение: реализация некопируемая, но перемещаемая, файл закрывается в деструкторе по RAII, отдельный
close()не обязателен; - тестируемость: обработчик данных зависит от интерфейса, поэтому в тестах подставляется реализация поверх буфера в памяти.
Затем реализации. Базовая на std::ifstream: открывать с std::ios::binary, читать read() в буфер и брать фактический объём из gcount(). Буфер в 4 КБ маловат, на последовательном чтении разумно брать от 64 КБ до нескольких мегабайт и выделять его в куче, а не на стеке. Размер файла даёт std::filesystem::file_size, смещения храните в std::streamoff или std::uintmax_t, а не в int: позиции за 2 ГБ его переполнят.
Когда нужен произвольный доступ или повторные проходы, файл отображают в память: mmap на POSIX, CreateFileMapping и MapViewOfFile на Windows, кросс-платформенно через boost::iostreams::mapped_file. Страницы подгружает ядро по требованию, копирования в пользовательский буфер нет. Для строковых форматов std::getline читает по строке и не держит файл целиком.
Где спотыкаются
Читают файл в строку одним махом, через std::istreambuf_iterator или std::stringstream. На больших файлах это исчерпание памяти, и интерфейс теряет смысл.
Открывают двоичный файл в текстовом режиме. На Windows это портит переводы строк и сбивает смещения.
Проверяют конец файла через while (!file.eof()) перед чтением. Последняя итерация обрабатывает мусор или пустой буфер: проверять нужно результат самой операции чтения и gcount().