Есть ли случаи, когда fseek/ftell может указывать неправильный размер файла?

В C или C++ для получения размера файла можно использовать следующее:

const unsigned long long at_beg = (unsigned long long) ftell(filePtr);
fseek(filePtr, 0, SEEK_END);
const unsigned long long at_end = (unsigned long long) ftell(filePtr);
const unsigned long long length_in_bytes = at_end - at_beg;
fprintf(stdout, "file size: %llu\n", length_in_bytes);

Существуют ли среды разработки, компиляторы или операционные системы, которые могут возвращать неправильный размер файла из этого кода, основываясь на заполнении или другой информации, зависящей от ситуации? Были ли изменения в спецификации C или C++ в 1999 году, что привело бы к тому, что этот код больше не работал в определенных случаях?

Для этого вопроса, пожалуйста, предположите, что я добавляю поддержку больших файлов, компилируя с флагами -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1, Благодарю.

4 ответа

Решение

Это не будет работать на непривычных файлах, таких как /proc/cpuinfo или же /dev/stdin или же /dev/tty или труба файлы, полученные с popen

И это не сработает, если этот файл будет записан другим процессом одновременно.

Использование статовой функции Posix, вероятно, более эффективно и надежно. Конечно, эта функция может быть недоступна в системах, отличных от Posix.

fseek а также ftell обе функции определены стандартом языка ISO C.

Ниже приводится последняя открытая версия стандарта C 2011 года, но стандарты ISO C 1990, 1999 и 2011 годов очень похожи в этой области, если не идентичны.

7.21.9.4:

Функция ftell получает текущее значение индикатора положения файла для потока, на который указывает поток. Для двоичного потока значение представляет собой количество символов в начале файла. Для текстового потока его индикатор положения файла содержит неопределенную информацию, используемую функцией fseek для возврата индикатора положения файла для потока на его позицию во время вызова ftell; разница между двумя такими возвращаемыми значениями не обязательно является значимым показателем количества написанных или прочитанных символов.

7.21.9.2:

Функция fseek устанавливает индикатор положения файла для потока, на который указывает поток. Если происходит ошибка чтения или записи, устанавливается индикатор ошибки для потока и происходит сбой fseek.

Для двоичного потока новая позиция, измеренная в символах от начала файла, получается путем добавления смещения к позиции, указанной в wherece. Указанная позиция является началом файла, если значение откуда SEEK_SET, текущим значением индикатора позиции файла, если SEEK_CUR, или концом файла, если SEEK_END. Бинарный поток не обязательно должен поддерживать вызовы fseek со значением откуда SEEK_END.

Для текстового потока либо смещение должно быть равно нулю, либо смещение должно быть значением, возвращаемым более ранним успешным вызовом функции ftell в потоке, связанном с тем же файлом, и откуда должно быть SEEK_SET.

Нарушение любого из положений "will" делает поведение вашей программы неопределенным.

Так что, если файл был открыт в двоичном режиме, ftell дает количество символов от начала файла - но fseek относительно конца файла (SEEK_END) не обязательно имеет смысл. Это подходит для систем, которые хранят двоичные файлы в целых блоках и не отслеживают, сколько было записано в последний блок.

Если файл был открыт в текстовом режиме, вы можете искать начало или конец файла со смещением 0, или вы можете искать позицию, заданную более ранним вызовом ftell; fseek с любыми другими аргументами имеет неопределенное поведение. Это подходит для систем, в которых количество символов, считываемых из текстового файла, не обязательно соответствует количеству байтов в файле. Например, в Windows чтение пары CR-LF ("\r\n") читает только один символ, но продвигает 2 байта в файле.

На практике в Unix-подобных системах текстовый и двоичный режимы ведут себя одинаково, и метод fseek/ftell будет работать. Я подозреваю, что это будет работать на Windows (я думаю, что ftell даст смещение в байтах, которое может не совпадать с количеством звонков getchar() в текстовом режиме).

Обратите внимание, что ftell() возвращает результат типа long, В системах, где long 32 бита, этот метод не может работать для файлов размером 2 ГБ или более.

Возможно, вам лучше использовать какой-то системный метод, чтобы получить размер файла. Поскольку метод fseek/ftell в любом случае зависит от системы, например stat() в Unix-подобных системах.

С другой стороны, fseek а также ftell вероятно, будет работать так, как вы ожидаете, на большинстве систем, с которыми вы можете столкнуться. Я уверен, что есть системы, где это не будет работать; извините, но у меня нет подробностей.

Если работа в Linux и Windows достаточно хороша, и вы не имеете дело с большими файлами, то метод fseek/ftell, вероятно, подходит. В противном случае вам следует рассмотреть возможность использования системного метода для определения размера файла.

И имейте в виду, что все, что говорит вам о размере файла, может сказать вам только его размер в этот момент. Размер файла может измениться, прежде чем вы получите к нему доступ.

1) Внешне ваш код выглядит нормально - я не вижу никаких проблем с ним.

2) Нет - не существует "спецификации C или C++", которая бы влияла на fseek. Существует спецификация Posix:

3) Если вы хотите "размер файла", мой первый выбор, вероятно, будет "stat ()". Вот спецификация Posix:

4) Если с вашим методом что-то пошло не так, то первым делом я бы подумал: "Поддержка больших файлов".

Например, многие ОС имели параллельные API-интерфейсы fseek() и fseek64().

"Надеюсь, что помогает.. PSM

POSIX определяет возвращаемое значение из fseek как "измеренное в байтах от начала файла". Ваш at_beg всегда будет нулевым (при условии, что это новый открытый файл).

Итак, предполагая, что:

  1. файл доступен для поиска
  2. нет проблем параллелизма
  3. размер файла представлен в типе данных, используемом выбранным вами вариантом fseek/ftell

тогда ваш код должен работать в любой POSIX-совместимой системе.

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