fwrite, fgets binary file readback problem

J

Jeff

Im trying to figure out why I cant read back a binary file correctly.

I have the following union:

#define BITE_RECORD_LEN 12
typedef union {
unsigned char byte[BITE_RECORD_LEN];
struct {
unsigned char type; /* 0 Type */
unsigned char sn[3]; /* 1-3 Serial Number */
unsigned char date_yy; /* 4 Date - Year */
unsigned char date_mm; /* 5 Date - Month */
unsigned char date_dd; /* 6 Date - Day */
unsigned char flight_leg; /* 7 Number */
unsigned char time_hh; /* 8 Time of Day - Hours */
unsigned char time_mm; /* 9 Time of Day - Minutes
*/
unsigned char etime[2]; /* 10-11 Elapsed Time */

struct { /* 12 Corrective Action
Status */
unsigned char spare: 6; /* bits 5-0, Spare */
unsigned char maint_alert: 1; /* bit 6, Alert,
1 = alert */
unsigned char serviced: 1; /* bit 7, Status,
1 = fault serviced */
} caa_status;
} field;
} myrecord;

I open it with: (some details ommitted)

fp = fopen(filename,"wb");

I write it as:

myrecord temp_fault_record;
fwrite(.byte, BITE_RECORD_LEN, 1, fp);

my call to ReadFile:
fopen(file, "rb");

m_fault_record = (myrecord *) malloc (size);
int num = ReadFile (file_des, (unsigned char *)m_fault_record,
size);

I read it back via fgets:

int ReadFile(FILE *fp, unsigned char *buf, int count)
{
if (fgets((char *)buf, count, fp) == NULL)
{
debug_message("ERROR ReadFile reading data");
return -1;
}
return count;
}

My problem is that after about 6 records (I loop over the
'm_fault_record')
my data is not valid. 'size' above is calculated by stat'ing the file
being read
and is correct.

At this point Ive spent several hours parsing down the code to a small
executable and trying to find the problem with no luck, which is why
Im posting
this here to ask. Ive looked at google for a while too for fgets. I
saw some people
talking about fread() for binary files, but it seemed more for
efficiency than a need
in my case.

If Ive missed any details please just reply and Ill add them. I think
I got everything.
I appreciate any and all comments. Someone else may see what Im doing
wrong.

As a sidenote, Im porting this code from c to c++ (which shouldnt be a
problem).
The old code used CVI/Labview for the ReadFile call, which I dont have
and I just
ported it to what I thought would be equivalent, and it should be
easy, but apparently
its not been.

Thanks,
Jeff
 
B

Ben Pfaff

Jeff said:
Im trying to figure out why I cant read back a binary file correctly.
[...]

if (fgets((char *)buf, count, fp) == NULL)

fgets is not appropriate for reading binary data. Use fread.
 
W

Walter Roberson

Im trying to figure out why I cant read back a binary file correctly.
I have the following union:
#define BITE_RECORD_LEN 12
typedef union {
unsigned char byte[BITE_RECORD_LEN];
struct {
unsigned char type; /* 0 Type */
unsigned char sn[3]; /* 1-3 Serial Number */
unsigned char date_yy; /* 4 Date - Year */
unsigned char date_mm; /* 5 Date - Month */
unsigned char date_dd; /* 6 Date - Day */
unsigned char flight_leg; /* 7 Number */
unsigned char time_hh; /* 8 Time of Day - Hours */
unsigned char time_mm; /* 9 Time of Day - Minutes
*/
unsigned char etime[2]; /* 10-11 Elapsed Time */

struct { /* 12 Corrective Action
Status */
unsigned char spare: 6; /* bits 5-0, Spare */
unsigned char maint_alert: 1; /* bit 6, Alert,
1 = alert */
unsigned char serviced: 1; /* bit 7, Status,
1 = fault serviced */
} caa_status;
} field;
} myrecord;

Check that sizeof myrecord.field is the same as BITE_RECORD_LEN
(and it wouldn't hurt to crosscheck that sizeof myrecord is BITE_RECORD_LEN)

Compilers are allowed to put unnamed padding between structure elements
for any purpose (though there are some restrictions on bitfield padding.)
I would not -expect- padding between consequative unsigned char, but
it is a possibility. In particular, I would be concerned that the
the caa_status struct might end up with padding before or after it.


I notice that you have used unsigned char bitfields. In C89/C90,
the base type for a bitfield must be a (possibly qualified)
int, signed int, or unsigned int. C99 adds _Bool to the allowed base
type list, along with "or some other implementation-defined type".
Thus the structure you show violates a Contraint unless you
are using C99 and your implementation defines char as an allowed base type.
The result *could* be that each of those unsigned char base types
in caa_status is silently converted to unsigned int, with caa_status
thus ending up as large as an int rather than "as large as a char"
that is implicit in your code. This is a concrete reason why your
field substructure might not be BITE_RECORD_LEN .
 
P

Peter Nilsson

Ben said:
fgets is not appropriate for reading binary data.

To be more precise, it's not appropriate for reading binary data that
contains zero bytes. Since it doesn't return a length of characters
read, there is no way to distinguish between a read null byte and
the null byte with which it terminates the buffer (unless it's the
last
character in the buffer.)

It will also stop reading if it encounteres a '\n', which is often
desired with text streams, but not so much with binary streams.

The fact that fgets doesn't cope with null bytes in streams is often
a good reason to ignore it for parsing text streams in programs
that need an extra level of robustness.
 
B

Ben Pfaff

CBFalconer said:
However there is no restriction against using getc (or fgetc),
which will convert the '\n's from text files correctly,

getc will not do any conversion on new-line characters in files
opened in binary mode, on the implementations with which I am
familiar.
and pass all the zero bytes. This makes the code depend only
on the actual file type. getc, if macro implemented, will be
just as fast, and eliminates the need for a buffer array.

On the implementations with which I am familiar, a sequence of
getc calls will not be as fast as a single fread or fgets call
that reads the same number of bytes. This is because every call
to getc must check whether the end of the file buffer has been
reached, but fread or fgets typically only needs to check for the
end of the buffer once.
 
R

Richard Tobin

CBFalconer said:
However there is no restriction against using getc (or fgetc),
which will convert the '\n's from text files correctly, and pass
all the zero bytes.

All the stdio functions will convert line ends in text mode, and not
in binary mode. There's no difference between getc(), fgets(), and
fread() in this respect.

-- Richard
 
B

Ben Pfaff

CBFalconer said:
That's what I thought I said. :)

No, you said that getc will convert '\n's from text file
correctly. I said no such conversion will happen when you open a
file in binary mode (which is what the OP's question is about).
 
R

Richard

Ben Pfaff said:
No, you said that getc will convert '\n's from text file
correctly. I said no such conversion will happen when you open a
file in binary mode (which is what the OP's question is about).

Semantics. "Correctly" can mean not translating at all depending on
mode. i.e Correct handling.
 
K

Keith Thompson

CBFalconer said:
On *ix, but not doze. Under *ix the files are identical, under
doze there are \r in the char sequence for text files, which will
be absorbed if followed by a \n.

Yes, in principle the conversion happens on any system. On Unix,
it just happens that it doesn't do anything.

The point is that you should still use "r" when opening a file in text
mode, and "rb" when opening a file in binary mode, even if your code
happens to run on a system where they happen to do the same thing.
This doesn't happen for a binary
file.

You mean for a file opened in binary mode.
And there are many other file systems.

Yes.
 
R

Richard Tobin

All the stdio functions will convert line ends in text mode, and
not in binary mode. There's no difference between getc(),
fgets(), and fread() in this respect.
[/QUOTE]
On *ix, but not doze. Under *ix the files are identical, under
doze there are \r in the char sequence for text files, which will
be absorbed if followed by a \n. This doesn't happen for a binary
file. And there are many other file systems.

I don't see the relevance of your comment. On both Unix and Windows,
there is no difference between getc(), fgets(), and fread() in respect
of how they handle line ends. They all convert them in text mode (and
the conversion is a no-op in Unix), and none of them convert them in
binary mode.

In particular, fread() on a text stream converts line-ends in just the
same way as getc(). fread() is not a "binary-only" function.

-- Richard
 
R

Richard Tobin

CBFalconer said:
If the raw file contains a cr lf sequence reading that file as text
under Windoze will return \n. Under Unix it will return \r\n. If
the file is opened as a binary file, Unix is unchanged, but Windoze
will return \r\n.

Once again I cannot see the relevance of your comment. I was saying
that there is no difference between getc(), fread(), and fgets(),
and you keep pointing out the difference between Unix and Windows.

-- Richard
 
A

Antoninus Twink

That's because his comment had no relevance. Maybe in the past CBF was a
reasonable programmer (though it's difficult to imagine based on the
code snippets he posts here), but today there's no doubt that senility
has set in and he's no longer capable of intelligent discussion. As
you're discovering, trying to point out his numerous mistakes is just a
recipe for frustration...
If you are unable to see that receiving different results on
different systems requires the programmer to make appropriate
adjustments, I suspect you should not be programming.

....and breathtakingly arrogant and misdirected insults.
 
R

Richard Tobin

CBFalconer said:
If you are unable to see that receiving different results on
different systems requires the programmer to make appropriate
adjustments, I suspect you should not be programming.

I give up. Your inability to follow a conversation makes it pointless
for others to talk to to you.

-- Richard
 
R

Richard Tobin

CBFalconer said:
However there is no restriction against using getc (or fgetc),
which will convert the '\n's from text files correctly, and pass
all the zero bytes. This makes the code depend only on the actual
file type. getc, if macro implemented, will be just as fast, and
eliminates the need for a buffer array.
This generated an attack on my answer by Mr Tobin, which I have
answered several times.

Your phrase "getc ... which will convert the \n's ... correctly" might
have been interpreted as meaning that there was something special
about getc(), in particular as compared with fread() which others had
mentioned. So I pointed out that there was no difference in how they
handled line ends.

I don't see how you can interpret that as an attack.

-- Richard
 
R

Richard

Richard Heathfield said:
CBFalconer said:


I think you owe Richard Tobin an apology for misunderstanding not only his
argument but also his motive.

I think you spend too much time telling people who to and when to
apologise.
 
K

Kenny McCormack

I think you spend too much time telling people who to and when to
apologise.

I have always assumed that someone had setup a web site (something like
www.clc.org/apologies) to keep track of who owes who what, debts
outstanding, debts paid, interest owed, etc).

Was I wrong?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top