What does the "size" argument in fread() refer to? - c

I'm trying to make a basic program which copies the header of 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);
fclose(input);
fclose(output);
}
However, after failing to make it work, I looked up how to do it and apparently
fread(header, sizeof(uint8_t), 44, input);
fwrite(header, sizeof(uint8_t), 44, output);
should be
fread(header, 44, 1, input);
fwrite(header, 44, 1, output);
This is very confusing to me, as I thought that the second argument was supposed to be the size of the type of data unit you want to fread. Can anyone help me understand what I'm missing? Many thanks.

The first size parameter specifies the size of a single record. The second size parameter defines how many records you want to load. fread returns the number of records it loaded.
So if you have a structure which is 20 bytes, and you have a file where you have stored 5 of the records then you can pass it like this.
if(fread(ptr, 20, 5, strm) != 5)
printf("Unable to load 5 records\n");
Alternativaly you could load the same file like this:
if(fread(ptr, 1, 5*20, strm) != 5*20)
printf("Unable to load 5 records\n");
The effect is the same, but the first version is more readable, because usually you may read records not just "bytes". It's easier to understand in the context of your application logic. And of course you don't have to deal with calculating the number of bytes yourself, thus reducing a chance for errors.
The same applies to fwrite.
So in your above example I would rather write it like this:
// Your header is 44 bytes long (which is sizeof) but you want to read ONE header
if(fread(header, sizeof(header), 1, input) != 1)
... error reading 1 header ....

sizeof(uint8_t) is guaranteed to be 1 . Some people think it good style to use the sizeof expression instead of the number.
You can either write 44 "blocks" of size 1, or 1 block of size 44. In either case , you tried to write 44 bytes -- the writing part is the same either way.
The difference is in the return value, which reports how many blocks were successfully written. So if you use the 1, 44 order then it will tell you exactly how many bytes were written. If you use the 44, 1 order then you will either get 0 or 1 as the return value (and no way to know if it partially succeeded).
The same consideration applies to fread.
The FILE* input = fopen("input.wav", "r"); part is incorrect: this specifies to open the file in text mode, which may perform translations on the input, such as changing the line endings.
Instead, open in binary mode with "rb" mode string.

Related

In cs50x labs volume.c, Why in do while reading samples we don't shift to end of header before reading samples?

CS50x Labs 4: volume.c
The problem is to change volume of a ".wav" file (variable: input; type: FILE) into another ".wav" file (variable: output; type:FILE).
We read the header of size 44 bytes using this code (as also given in the solution)
uint8_t header[HEADER_SIZE];
fread(header, HEADER_SIZE, 1, input);
fwrite(header, HEADER_SIZE, 1, output);
This is fine, reading 44 bytes from input then storing it into header and finally writing it to output file.
Further, for reading each sample the code is
int16_t buffer;
while (fread(&buffer, sizeof(int16_t), 1, input))
{
buffer = buffer * factor; //change volume
fwrite(&buffer, sizeof(int16_t), 1, output);
}
Now, A ".wav" file begins as
44 bytes header
sequence of samples (each 2 bytes) representing audio which needs to be multiplied for volume changes.
In the 2nd part of the code, we use while (fread(&buffer, sizeof(int16_t), 1, input)). It reads 2 bytes from input at a time and stores it in buffer. Which we later multiply (for volume change) and store it in output.
But, Isn't it also reading header (1st 44 bytes) 2 bytes at a time and multiplying it with factor as we haven't mentioned anywhere to start reading from 45th byte as that's where samples start from.
This is the solution given by CS50x team, it works properly. But, it still feels wrong.
1st to 44th byte: should remain as it is.
45th to ... byte: read 2 at a time and multiply by factor.
This doesn't seems to be happening here?

How to duplicate an image file? [duplicate]

I am designing an image decoder and as a first step I tried to just copy the using c. i.e open the file, and write its contents to a new file. Below is the code that I used.
while((c=getc(fp))!=EOF)
fprintf(fp1,"%c",c);
where fp is the source file and fp1 is the destination file.
The program executes without any error, but the image file(".bmp") is not properly copied. I have observed that the size of the copied file is less and only 20% of the image is visible, all else is black. When I tried with simple text files, the copy was complete.
Do you know what the problem is?
Make sure that the type of the variable c is int, not char. In other words, post more code.
This is because the value of the EOF constant is typically -1, and if you read characters as char-sized values, every byte that is 0xff will look as the EOF constant. With the extra bits of an int; there is room to separate the two.
Did you open the files in binary mode? What are you passing to fopen?
It's one of the most "popular" C gotchas.
You should use freadand fwrite using a block at a time
FILE *fd1 = fopen("source.bmp", "r");
FILE *fd2 = fopen("destination.bmp", "w");
if(!fd1 || !fd2)
// handle open error
size_t l1;
unsigned char buffer[8192];
//Data to be read
while((l1 = fread(buffer, 1, sizeof buffer, fd1)) > 0) {
size_t l2 = fwrite(buffer, 1, l1, fd2);
if(l2 < l1) {
if(ferror(fd2))
// handle error
else
// Handle media full
}
}
fclose(fd1);
fclose(fd2);
It's substantially faster to read in bigger blocks, and fread/fwrite handle only binary data, so no problem with \n which might get transformed to \r\n in the output (on Windows and DOS) or \r (on (old) MACs)

Why does the calling of dup2 go wrong?

As you see, the program has two file pointer sport and fruit point to the file fruit.txt. The problem is that after run the program, sport.txt is empty and fruit.txt contains Chinese characters. I expected that the sport.txt should contains the word "basketball" because it is written to the file before redirecting happens. So, what is wrong here?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "../cus_header/cus_header.h"
int main(){
FILE *fruit = fopen("fruit.txt", "w");
if(!fruit)
error("cannot open fruit.txt");
FILE *sport = fopen("sport.txt", "w");
if(!sport)
error("cannot open sport.txt");
int de_sport = fileno(sport);
int de_fruit = fileno(fruit);
printf("file number of sport.txt: %i and of fruit.txt: %i\n", de_sport, de_fruit);
fwrite("basketball", sizeof(char), 10, sport);
fwrite("apple", sizeof(char), 6, fruit);
if(dup2(de_fruit, de_sport) == -1)
error("cannot redirect");
fwrite("basketball", sizeof(char), 10, sport); //???
fwrite("apple", sizeof(char), 6, fruit); // ???
fclose(sport);
fclose(fruit);
return 0;
}
As the comments already mention, you shouldn't mix file manipulation with streams (using FILE*, fopen, fwrite, fclose) with raw file manipulation (using file descriptors, open, write, close, dup2). And especially don't mix them on the same file pointer/descriptor like you are doing in this piece of code.
Let's go through the code to see why it behaves the way it does:
FILE *fruit = fopen("fruit.txt", "w");
...
FILE *sport = fopen("sport.txt", "w");
You shouldn't care about how the FILE structure looks like, let's just suppose it keeps the underlying file descriptor somewhere.
int de_sport = fileno(sport);
int de_fruit = fileno(fruit);
You create local variables holding the same file descriptors as the two FILE* refer.
fwrite("basketball", sizeof(char), 10, sport);
fwrite("apple", sizeof(char), 6, fruit);
You write something in each of the two files. Because C file streams are buffered by default, the actual writing in the file on disk might not happen right away (and in your case it doesn't).
dup2(de_fruit, de_sport)
This closes the file descriptor de_sport and makes it refer to the same file as de_fruit. The actual numerical values remain the same, only the actual files that they refer to are changed. This means that the two FILE handles will write to the same file after the dup2 call.
fwrite("basketball", sizeof(char), 10, sport); //???
fwrite("apple", sizeof(char), 6, fruit); // ???
This will write to the same underlying file because the two descriptors now refer to the same file. But again, because streams are buffered, this might actually just append to the buffers of those two FILE*s.
fclose(sport);
fclose(fruit);
This flushes the buffers, so the actual writing to disk happens here. Because the descriptors have been changed, if no flushing happened until now, both streams will actually flush to the same file on disk.
This is probably why you're seeing that behavior, but keep in mind that what you're doing is not safe and that the behavior or file contents might differ.

How to make a variable return after exit in C

Okay so I have a program and it adds new entries to a structure to a .dat file. I know when appending it adds to the end of the list. This works fine. The problem I am having is that I have a tracking variable that keeps track of the length of the struct. I can bring back the items in the struct but how do I bring back the variable that is keeping track of the length. You can't find it's length either so I would have to bring back a variable. I was thinking leave the first line of the .dat file just for the tracking variable but I don't know if you can append just the first line.
Bellow is of course what I am using to add in items to my struct. Above it would be num ++ which means the length of the struct increased by one.
if ( pWrite != NULL ) {
fprintf(pWrite, "%s %s %s\n", info2[nume].first, info2[nume].last, info2[nume].number);
fclose(pWrite);
} else {
perror("The following error occurred");
exit(EXIT_FAILURE); //exit program with error
}
Not sure if I am leaving anything out. Any help would be great. Thanks!
Store an integer in the file which represents the amount of structs you have in the file.
[integer]xxxxxxxxxxxxxxxxxxxxxx
or
xxxxxxxxxxxxxxxxxxxxxx[integer]
In the both cases, updating would require the r+ flag when opening the file (→ fopen). Then, you can simply read and then overwrite the integer.
// This may not work - my C skills got worse over the time :)
FILE *f = fopen("test.dat", "r+");
fread(buffer, sizeof(int), 1, f);
fseek(f, 0, SEEK_START);
fwrite(buffer, sizeof(int), 1, f);
If the elements of the struct are of known length, then you can look at the file size and compute how many elements there must be. That is the easiest way to accomplish what you are asking.
EDIT After reading other attempted answers, and your comments, here is something that might work better for you - random file access. The following illustrates this:
FILE *fp;
int myCounter = -1;
fp = fopen("myFile.dat", "wb"); // want to write
fwrite(&myCounter, sizeof(int), 1, fp); // write size of -1 to start of file: "unknown count"
fprintf(fp, "Now I write something");
fseek(fp, 0, SEEK_SET); // point file to beginning
myCounter=1;
fwrite(&myCounter, sizeof(int), 1, fp); // write the new size: 1
fseek(fp, 0, SEEK_END); // back to the end of the file
fprintf(fp, "Here is something else");
... etc
It should be obvious how you can wrap this into a loop - note though that it would be slow to update the file counter on every write, since the disk will be "hunting" from one sector to another. So better write until you have no more to write, then update the counter at the start of the file with the fseek command. You do need to do the first write just to make sure you leave space. I chose to write -1 as this would indicate to the reading program "no valid size has been written". Usually good to put some error checking like that in your code...
If you haven't saved the variable's value somehow/somewhere (for example, written to a file), and can't recompute it from what you HAVE saved, its gone.

Why 0x4e0 instead of 0x0 in wav file head?

I have a wav file named "file.wav", and I want to read some specification of the file with a C program. I found the structure of wav file here. According to this document, in a normal wav file, the AudioFormat bytes should be 0x1 and 0x0, or the NumChannels bytes should be (if mono) 0x1 and 0x0; there are lots of similar sections that should look like these.
Now, my C program is very easy: I open the file in binary mode, I seek to the AudioFormat section (the 20th byte), I read two bytes and I put them in a buffer; then I print to stdin the buffer content.
#include <stdio.h>
main()
{
void *buf[2];
FILE *f;
f=fopen("file.wav", "rb");
fseek(f, 20, SEEK_SET);
fread(buf, 1, 2, f);
printf("example: %#hx %#hx\n", buf[0], buf[1]);
/*the '#' flag stands for the 0x[···] format output,
the 'h' says that the number is a short integer (2 bytes).*/
return 0;
}
My problem is that the second byte instead of being 0x0 is always 0x4e0, and this for every byte which should be 0 (for example for NumChannels or BitPerSamples bytes too). What is the problem?
If you were lucky, you would have got compilation warning or crash at runtime. Although I think for this particular case you won't, I don't think what you are doing is actually undefined behavior. The core of your problem is that when you declare this:
void *buf[2];
you are declaring buf as array 2 of pointer to void. When you access buf[0] you are not reading bytes but pointers. There is a lot more that could be said to explain what is going on, but I think this would be enough for what you are trying to do:
char buf[2];
As others has mentioned, you are reading into a pointer array.
Second is the audio format field is 2 bytes.
FILE *f;
short afmt;
if((f = fopen("file.wav", "rb")) == NULL)
err(1, "fopen");
fseek(f, 20, SEEK_SET);
fread(&afmt, sizeof(short), 1, f);
printf("audio format: 0x%hx\n", afmt);
fclose(f);

Resources