简体   繁体   中英

Why does printf(“%s”,charstr) increasingly prints more than expected with each fread()?

In an attempt to learn file structures, I am trying to read in a .wav file and simply print information about it. I have a struct that holds all the information defined as so:

typedef struct{
   char chunkId[4];
   unsigned int chunkSize;
   char format[4];
   char subchunk1Id[4];
   unsigned int subchunk1Size;
   unsigned short audioFormat;
   unsigned short numChannels;
   unsigned int sampleRate;
   unsigned int byteRate;
   unsigned short blockAlign;
   unsigned short bitsPerSample;
   char subchunk2Id[4];
   unsigned int subchunk2Size;
   void *data;
} WavFile;

What's happening is that for each time I fread through the file, It causes my c-strings to print longer and longer. Here's a sample code snippet:

   fseek(file, SEEK_SET, 0);
   fread(wavFile.chunkId, 1, sizeof(wavFile.chunkId), file);
   fread(&wavFile.chunkSize, 1, sizeof(wavFile.chunkSize), file);
   fread(wavFile.format, 1,sizeof(wavFile.format), file);  
   fread(wavFile.subchunk1Id, 1, sizeof(wavFile.subchunk1Id), file);
   fread(&wavFile.subchunk1Size, 1, sizeof(wavFile.subchunk1Size), file);
   fread(&wavFile.audioFormat, 1, sizeof(wavFile.audioFormat), file);

   printf("%s\n",wavFile.chunkId);
   printf("%d\n",wavFile.chunkSize);
   printf("%s\n",wavFile.format);
   printf("%s\n",wavFile.subchunk1Id);
   printf("%d\n",wavFile.subchunk1Size);
   printf("%d\n",wavFile.audioFormat);

Something in the way I have my struct setup, the way I'm reading the file, or the way that printf() is seeing the string is causing the output to print as shown:

RIFF�WAVEfmt 
79174602
WAVEfmt 
fmt 
16
1

The expected output:

RIFF
79174602
WAVE
fmt
16
1

I do understand that c-strings need to be null terminated, but then I got to thinking how is printing a string from a binary file any different from printing a string literal like printf("test"); ? The file specifications requires that the size of the members to have the exact sizes defined in my struct . Doing char chunkId[5]; and then chunkId[4]='\\0'; won't seem to be a good solution to this problem.

I've been trying to resolve this for a couple days now, so now I'm coming to SO to maybe get a push in the right direction.

For full disclosure, here's the hex output of the relevant portion of the file because this webform doesn't show all garbled mess that is showing up on my output.

52 49 46 46 CA 1B B8 04 57 41 56 45 66 6D 74 20 10 00 00 00 01 00 02 00 44 AC 00 00 98 09 04 00 06 00 18 00 64 61 74 61

If you know the size, you can limit the output of printf :

// Only prints 4-bytes from format.  No NULL-terminator needed.
printf("%.4s\n", wavFile.format);

If the size is stored in a different field, you can use that too:

// The * says: print number of chars, as dictated by "theSize"
printf("%.*s\n", wavFile.theSize, wavFile.format);  

The way you have called printf() , it expects a '\\0' terminated string, but your struct elements aren't ( fread() doesn't add '\\0' and format , chunkId etc. don't have enough length to contain it).

The simplest way is:

 printf( "%.*s\n", (int)sizeof(wavFile.format), wavFile.format );

If it is not a null terminated string you can use .* and an extra int argument which specifies the size of the string to printf , for example:

 printf("%.*s\n", (int)sizeof(wavFile.chunkId), wavFile.chunkId);

or alternatively:

 printf("%.4s\n", wavFile.chunkId);

which in your case may be simpler since the size seems to be fixed in your case.

From the printf document above the precision specifier in the format string works as follows:

(optional) . followed by integer number or * that specifies precision of the conversion. In the case when * is used, the precision is specified by an additional argument of type int. If the value of this argument is negative, it is ignored. See the table below for exact effects of precision.

and the table below which this text references says the following for character string :

Precision specifies the maximum number of bytes to be written.

First, be sure you're reading the file in binary mode (use fopen with the mode set to "rb" ). This makes no difference on Unix-like systems, but on others reading a binary file in text mode may give you corrupted data. And you should be checking the value returned by each fread() call; don't just assume that everything works.

printf with a %s format requires a pointer to a string. A string always has a null character '\\0' to mark the end of it.

If you have a chunk of data read from a file, it's unlikely to have a terminating null character.

As the other answers say, there are variations of the %s format that can limit the number of character printed, but even so, printf won't print anything past the first null character that happens to appear in the array. (A null character, which is simply a byte with the value 0, may be valid data, and there may be more valid data after it.)

To print arbitrary character data of known length, use fwrite :

fwrite(wavFile.chunkId, sizeof wavFile.chunkId, 1, stdout);
putchar('\n');

In this particular case, it looks like you're expecting chunkId to contain printable characters; in your example, it has "RIFF" (but without the trailing null character). But you could be reading an invalid file.

And printing binary data to standard output can be problematic. If it happens to consist of printable characters, that's fine, and you can assume that everything is printable in an initial version. But you might consider checking whether the characters in the array actually are printable (see isprint() ), and print their values in hexadecimal if they're not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM