简体   繁体   中英

What is the difference between looping fread() over fwrite() vs using them separately?

(Note: Apologies to anyone who read my previous post from half an hour ago- I've realised that I posted the wrong half of the code, and it was actually the second half that was giving me problems. So, if this post seems familiar it probably is!)

I'm trying to make a basic program which copies a .wav file, this was my attempt:

#include <stdio.h>
#include <stdint.h>

int main (void)
{
    FILE* input = fopen("input.wav", "r");
    FILE* output = fopen("output.wav", "w");
    
    uint8_t header [44];
    
    fread(header, sizeof(uint8_t), 44, input);
    fwrite(header, sizeof(uint8_t), 44, output);
    
    int16_t body;
    
    fread(&body, sizeof(int16_t), 1, input);
    fwrite(&body, sizeof(int16_t), 1, output);
    
    fclose(input);
    fclose(output);
}

However, after failing to make it work, I looked up how to do it and apparently

fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);

should be

while (fread(&body, sizeof(int16_t), 1, input))
{
    fwrite(&body, sizeof(int16_t), 1, output);
}

Why is it that the first part can copy the header without a problem, but the second part has to use a loop in order to work? To my eyes, they appear to be doing the same thing.

I think the fundamental issue here is that your code for reading and writing the header is reading the 44-byte header all at once, whereas the code for reading and writing the body is reading and writing just two bytes. But the body of the wav file is probably much bigger than two bytes!

Specifically, this code:

fread(header, sizeof(uint8_t), 44, input);
fwrite(header, sizeof(uint8_t), 44, output);

reads and writes a 44-byte header, all at once. But this code:

fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);

reads and writes two bytes. If the body is just two bytes long, that's fine. But if not, it's not going to copy the entire body; it's just going to copy the first two bytes.

This code, on the other hand, reads and writes multiple two-byte objects, as many of them as there are:

while (fread(&body, sizeof(int16_t), 1, input) == 1)
    fwrite(&body, sizeof(int16_t), 1, output);

If you knew how big the body was, you could read and write it all at once, also (that is, just like you did with the header):

uint8_t body[body_size];
fread(body, 1, body_size, input);
fwrite(body, 1, body_size, output);

Or, if you know the body size as 16-bit words, and you want to think about it that way, you could do

uint16_t body [body_size_in_words];
fread(body, 2, body_size_in_words, input);
fwrite(body, 2, body_size_in_words, output);

fread and fwrite can be confusing because they're designed to let you pretend you're reading and writing something other than individual bytes. There are sort of three different ways to use them:

  • fread(buf, 1, n, fp) read n bytes
  • fread(buf, size, 1, fp) read one record of size bytes
  • fread(buf, size, n, fp) read n records of size bytes

Deep down, fread basically just multiplies n * size , and tries to read that many bytes. Then, however many bytes it reads, it divides that number by size before returning it to you -- that is, it returns the number of records read, not necessarily the number of bytes read. And the same examples, explanations, and arguments hold for fwrite , of course.

Have a look at the read and write functions. As you can see, the signatures of both functions are very similiar and use the same arguments. Both are used to operate on blocks of data.

 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Argument Description
*ptr Points to the buffer where the data is stored
size The size of each block in bytes
nmemb The amount of blocks to read/write
*stream Points to the structure which describes the outgoing stream

So, the difference in your code is:

// will read ONE block of data with the size of int16_t
fread(&body, sizeof(int16_t), 1, input);
// will read 44 blocks of data with the size of uint8_t
fread(header, sizeof(uint8_t), 44, input);

// will write ONE block of data with the size of int16_t
fwrite(&body, sizeof(int16_t), 1, output);
// will write 44 blocks of data with the size of int16_t
fwrite(header, sizeof(uint8_t), 44, output);

Both methods return a value of type size_t that reflects the amount of bytes which were read or wrote. If they return 0, nothing happened. As you know everything unequal to 0 is considered to be true in C. So to answer your question:

// reads ONE Block of size int16_t at a time and stores it in body
// => will return 0 and stop the loop if nothing was read anymore
while (fread(&body, sizeof(int16_t), 1, input))
{
    // writes ONE block of size int16_t from body to output
    fwrite(&body, sizeof(int16_t), 1, output);
}

The loop will continue as long as there is data left to read, then stop. The header of the file has a fixed size, as it is standardized. The rest of the file has a variable length, hence the loop.

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