2016-12-19 2 views
3

В последнее время я наткнулся на сообщение: How to read an entire file into memory in C++, где описаны различные методы чтения файлов. Каждый подход прокомментирует его эффективность или риски, связанные с неопределенным поведением . Среди списка следующий пример представлен:Чтение файлов и неопределенное поведение

// Bad code; undefined behaviour 
in.seekg(0, std::ios_base::end); 

Которые в основном в той или подобной форме часто используется на самом деле читать размер файла. Рассуждение представлены в посте, в общем, то, что в C standard (N1570) §7.21.3 говорится, что:

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

Что footnote 268 для:

Файл не должен начинаться, ни заканчиваться в начальном состоянии сдвига

Для подтверждения выше для C++ 11 есть дополнительная ссылка C++ standard draft (N3242) 27.9.1.1, в котором говорится:

Ограничения на чтение и запись последовательности, контролируемой Объект класса basic_filebuf те же, что и для чтения и записи со стандартными файлами F библиотеки.

Где basic_filebuf согласно cppreference является частью реализации для basic_ifstream (внутреннего буфера). Что указывает на то, что реализация ifstream должна быть также обременена обозначенным поведением.

Из того, что я понял из описания и из того, что мне удалось выкопать, эта проблема в основном связана с широко ориентированными потоками, которые могут не заканчиваться в initial shift state.

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

  • Что на самом деле initial state shift? Я предполагаю, что это не может быть связано с кластерами данных. Больше для многобайтовой кодировки символов, но таким образом проблема не ограничивалась бы только не двоичными потоками?
  • Когда мы имеем дело с wide- и narrow-oriented потоками? Я знаю, что: "A newly opened stream has no orientation." и ориентация определяется при первом вызове ввода-вывода в потоке. Но на практике есть, скажем, любые значения по умолчанию, зависящие от типа потока, системы, локали или чего-то еще?
  • Если это еще не ответили или не обозначено, что является практическим примером того, когда такое неопределенное поведение может произойти? В том смысле, что можно воспроизвести его.

ответ

3

"shift state" - ограниченный действительно для многобайтовых текстовых потоков (и лечения EOL \r\n против \n) и этой проблемы будет ограничен текст потоки на самом деле.

Но это не только проблема. Из статьи, которую вы указали, с моим упором:

Некоторые платформы хранят файлы в виде записей фиксированного размера. Если файл короче размера записи, остальная часть блока дополняется. Когда вы ищете «конец», ради эффективности, он просто прыгает прямо до конца последнего блока ... возможно, длинный после фактического конца данных, после связки отступов.

fseek(p_file, 0, SEEK_END) с последующим ftell(...) дает правильный ответ только до тех пор, как EOF флаг не поднимается.
Прочитайте раздел «Решение (действительно большие файлы)», в котором приведены подробности, в частности:
, шаг 4. «Восстановите поток до начальной позиции с seekg(). Это также очистит флаг EOF.«

Вопрос в комментариях:

у вас есть знания, какие платформы в частности?

Google приземлился на this list файловых систем, ориентированных на запись - в основном мэйнфреймы, некоторые из которых все еще используются.

Другая область, которая может быть областью «записей»: «облако». Вы никогда не знаете, когда кто-то собирается (re) воплотить Distributed Data Management Architecture и попадает в проблемы, разрешенные record-oriented files. Для всего, что я знаю (что почти ничего), NFS может уже это сделать: RFC говорит о «блокировке записи».

Один из лучших, я бы обратил внимание на стандарт и относился к этому вопросу с уважением при написании «действительно стандартного, кросс-платформенного совместимого программного обеспечения» в C/C++.

+0

Вы правы, я тоже это видел. Это сделало бы приобретенный размер неправильным. Тем не менее вывод заключался в следующем: «Вы можете получить количество символов с ftell() в двоичном режиме ... но вы не можете искать конец файла с помощью fseek (p_file, 0, SEEK_END). Кроме того, некоторые платформы хранить файлы в виде записей фиксированного размера. У вас есть знания, какие платформы особенно? Решение, представленное автором с использованием 'gcount', выглядит действительным. Тем не менее, меня больше интересует, где подход 'seek to to end-to-get-size' может оказаться неудачным на практике. – Dusteh

+0

@ Dusteh Дополнительная информация размещена в ответе. Говоря это, я действительно не знаю, как воспроизвести «доступ к файлам с записью» на обычных доступных мне OS-es. –

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