Есть ли случаи, когда 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
всегда будет нулевым (при условии, что это новый открытый файл).
Итак, предполагая, что:
- файл доступен для поиска
- нет проблем параллелизма
- размер файла представлен в типе данных, используемом выбранным вами вариантом fseek/ftell
тогда ваш код должен работать в любой POSIX-совместимой системе.