Почему fread рано достигает EOF?

Я пишу библиотеку C, которая читает файл в память. Он пропускает первые 54 байта файла (заголовка), а затем считывает остаток как данные. Я использую fseek для определения длины файла, а затем использую fread для чтения в файле.

Цикл выполняется один раз, а затем завершается, поскольку достигнут конец EOF (без ошибок). В конце bytesRead = 10624, ftell (stream) = 28726, а буфер содержит 28726 значений. Я ожидаю, что fread прочитает 30 000 байт, а позиция файла будет 30054 при достижении EOF.

C - не мой родной язык, поэтому я подозреваю, что где-то у меня есть глупая ошибка новичка.

Код выглядит следующим образом:

const size_t headerLen = 54;

FILE * stream;
errno_t ferrno = fopen_s( &stream, filename.c_str(), "r" );
if(ferrno!=0) {
  return -1;
}

fseek( stream, 0L, SEEK_END );
size_t bytesTotal = (size_t)(ftell( stream )) - headerLen; //number of data bytes to read
size_t bytesRead = 0;
BYTE* localBuffer = new BYTE[bytesTotal];
fseek(stream,headerLen,SEEK_SET);
while(!feof(stream) && !ferror(stream)) {
    size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-bytesRead,stream);
    bytesRead+=result;
}

В зависимости от ссылки, которую вы используете, вполне очевидно, что добавление буквы «b» к флагу режима является ответом. Ищу кандидатуры на нагрудный знак. :-)

Эта ссылка говорит об этом во втором абзаце, втором предложении (хотя и не в их таблице).

MSDN не обсуждает двоичный флаг до середины вниз по странице.

OpenGroup упоминает о существовании тега "b", но заявляет, что он "не имеет никакого эффекта".


person James Schek    schedule 02.10.2008    source источник
comment
Я добавил тег microsoft, так как fopen_s есть только в MS C RTL.   -  person user7116    schedule 02.10.2008
comment
OpenGroup упоминает об этом: r или rb - Открыть файл для чтения. ... Символ 'b' не действует, но допускается для соответствия стандарту ISO C. Пожалуйста, исправьте вашу правку.   -  person Jonathan Leffler    schedule 03.10.2008
comment
Определив размер файла и выделив ему память, вы сможете прочитать весь файл за одну операцию чтения.   -  person EvilTeach    schedule 13.12.2010


Ответы (3)


возможно, это проблема с двоичным режимом. Попробуйте открыть файл с "r+b" в качестве режима.

РЕДАКТИРОВАТЬ: как указано в комментарии, "rb", вероятно, лучше соответствует вашему первоначальному замыслу, поскольку "r+b" откроет его для чтения / записи, а "rb" - только для чтения.

person Evan Teran    schedule 02.10.2008
comment
Я согласен с Эваном, что это, вероятно, проблема с двоичным режимом. Однако я почти уверен, что стандарт C не гарантирует, что ftell, когда вы его используете, вернет фактическую длину файла. Кажется, я помню, что требуется вернуть токен, который вернет вас в ту же позицию в файле, если вы передадите его в fseek. - person Paul Tomblin; 02.10.2008
comment
+1 для Майка Ф и меня. Windows все время кусает меня с помощью + b. - person user7116; 02.10.2008
comment
Я бы посоветовал сначала попробовать rb, так как r + b открывает файл для чтения и записи, и если вы не собираетесь записывать в файл, вы должны продолжать открывать его как доступный только для чтения. - person Greg Hewgill; 02.10.2008
comment
У меня была такая же мысль ... Согласно этой ссылке, это фактическая позиция: opengroup.org/onlinepubs/009695399/functions/ftell.html - person James Schek; 02.10.2008
comment
Это ответ. rb работает. Еще один случай RTFM. Об этом также говорится в первом абзаце: cplusplus.com/reference/clibrary/cstdio/fopen .html - person James Schek; 02.10.2008
comment
Вау, разве не удивительно, что нам все еще приходится иметь дело с этими проблемами с двоичными и текстовыми файлами в 2008 году. Готов поспорить, если вы посмотрите в свой файл в позиции 10624, вы увидите 0x1B (десятичное 27), что является концом файла. персонаж. - person David Smith; 02.10.2008
comment
@ Дэвид Смит: 0x1B - это персонаж Escape. Старый символ конца файла DOS был 0x1A (^ Z). - person Greg Hewgill; 02.10.2008
comment
Спасибо, 6,5 лет спустя, и этот пост мне тоже помог. - person rsethc; 16.01.2015
comment
вы, сэр, просто сэкономили мне много времени! +1 - person meetaig; 17.10.2016

Также стоит отметить, что простое включение binmode.obj в вашу команду ссылки сделает это за вас при открытии всех файлов.

person Richard Harrison    schedule 02.10.2008

Решение, основанное на предыдущих ответах:

    size_t bytesRead = 0;
    BYTE* localBuffer = new BYTE[bytesTotal];
    fseek(stream,headerLen,SEEK_SET);
        while(!feof(stream) && !ferror(stream)) {
        size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-
        bytesRead,stream);
    bytesRead+=result;
}
person Kumar Pushkar    schedule 26.06.2014