29
votes

I am writing a C library that reads a file into memory. It skips the first 54 bytes of the file (header) and then reads the remainder as data. I use fseek to determine the length of the file, and then use fread to read in the file.

The loop runs once and then ends because the EOF is reached (no errors). At the end, bytesRead = 10624, ftell(stream) = 28726, and the buffer contains 28726 values. I expect fread to read 30,000 bytes and the file position to be 30054 when EOF is reached.

C is not my native language so I suspect I've got a dumb beginner mistake somewhere.

Code is as follows:

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;
}

Depending on the reference you use, it's quite apparent that adding a "b" to the mode flag is the answer. Seeking nominations for the bonehead-badge. :-)

This reference talks about it in the second paragraph, second sentence (though not in their table).

MSDN doesn't discuss the binary flag until halfway down the page.

OpenGroup mentions the existance of the "b" tag, but states that it "shall have no effect".

3
I added the microsoft tag since fopen_s is only in MS's C RTL.user7116
OpenGroup does mention it: r or rb - Open file for reading. ... The character 'b' shall have no effect, but is allowed for ISO C standard conformance. Please fix your edit.Jonathan Leffler
Having found the file size, and allocated the memory, You should be able to read the entire file in, with one fread.EvilTeach

3 Answers

55
votes

perhaps it's a binary mode issue. Try opening the file with "r+b" as the mode.

EDIT: as noted in a comment "rb" is likely a better match to your original intent since "r+b" will open it for read/write and "rb" is read-only.

0
votes

Also worth noting that simply including binmode.obj into your link command will do this for you for all file opens.

-1
votes

A solution, based on the previous answers:

    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;
}