I am working on a project that involves reading binary data from a file into certain data structures. While testing, I saw that incorrect data was being loaded into these structures. Adding a little debug code (using ftell) revealed that fread was not starting at the beginning of the file, but at some offset hundreds of bytes deep. What could be causing this?
I have tried adding fseek(infile, 0, SEEK_SET); before the first fread call, but the first call still started at the same offset as before. I also tried using rewind(infile) to no avail. I did see that whether this problem occurred depended on the file being read. Some files would always start at position 0 while others would always start at some other offset.
Here is a minimal example of code that exhibits this problem on my machine. I am currently running Windows 10 and the code was compiled in Visual Studio.
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE* infile;
char* inname;
char x;
inname = argv[1];
if ( (fopen_s(&infile, inname, "r")) != 0) {
printf("Error opening file: %s\n", inname);
exit(1);
}
if (infile == 0) {
printf("Error opening file.\n");
exit(1);
}
while (fread(&x, sizeof(char), 1, infile) == 1) {
printf("%ld\n", ftell(infile));
printf("%hhx\n\n", x);
}
fclose(infile);
return 0;
}
You should open the file in binary read mode.
if ( (fopen_s(&infile, inname, "r")) != 0) {
to
if ( (fopen_s(&infile, inname, "rb")) != 0) {
From fopen man
The mode string can also include the letter 'b' either as a last
character or as a character between the characters in any of the
two-character strings described above. This is strictly for
compatibility with C89 and has no effect; the 'b' is ignored on all
POSIX conforming systems, including Linux. (Other systems may treat
text files and binary files differently, and adding the 'b' may be a
good idea if you do I/O to a binary file and expect that your program
may be ported to non-UNIX environments.)
Related
CS50 Lab4 code which change volume of .wav file:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
const int HEADER_SIZE = 44;
uint8_t header[HEADER_SIZE];
int16_t buffer;
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
float factor = atof(argv[3]);
fread(header, 1, HEADER_SIZE, input);
fwrite(header, 1, HEADER_SIZE, output);
int n = 0; // for debug purpose
while (fread(&buffer, 2, 1, input))
{
buffer = buffer * (float)factor;
fwrite(&buffer, 2, 1, output);
if (n == 2115) // for debug purpose
{
if (n == 2111) // for debug purpose
;
}
printf("%d\n", n++); // for debug purpose
}
// Close files
fclose(input);
fclose(output);
}
The thing is.. That it works perfectly in CS50 Codespace IDE:
but all my local compilers (I've tried: bcc32, cpp32, tcc, gcc, clang) gives the same result - output of this broken file (must be 345kb file, but it's 5 kb):
https://cdn.discordapp.com/attachments/792992622196424725/964833387363852308/output.wav
I've tried some debug:
According debug it always stops at 2117 step (4608 buffer value).
Again I wanna point that in CS50 IDE it works alright and it goest through all 176399 steps :)
feof(input) and ferror() debug:
Please help to solve this puzzle! I can't rest until I understand whats wrong there..
On Windows, you must open binary files with mode rb (or wb for writing). On Unix, you should do that for portability, but in practice it works either way. (And it has nothing to do with the compiler).
The reason is that in text files, Windows treats a byte with value 0x1A (which is Ctl-Z) as an EOF indicator. Unix doesn't do this; on Unix, the end of the file is where the file ends.
Also, Windows uses a two-character end-of-line indicator (\r\n), which must be translated to a single \n because the C standard requires that multi-character end-of-line indicators in a text file be translated to a single newline character (and translated back when you write to the file). That doesn't happen on Unix either, because Unix line endings are already a single newline character.
So on Windows, if you read a binary file without specifying the b-for-binary open mode, then the read will stop at the first 0x1A in the file. In your case, that seems to have happened on the 2117th character read, but note that that might not be the 2117th character in the file because of newline translation. You could try looking at your file with a binary editor, but the bottom line is that if you think your program might be run under Windows, then you should always use rb and wb for binary files. Unix ignores the b and it tells Windows to stop messing with your file.
I'm trying to transfer a file from server to client. I first send the name of the file I want to receive to the server, the server opens the file and writes its contents into a buffer and sends it back to the client. The client then copies the contents of that buffer into a newly created file to duplicate the contents of the server file.
When "Receive.txt" is created, only the FIRST word of the file I opened is copied into the file I created. Where am I going wrong?
server.c sending part:
if(checkCommand){
char *tmp = buf + 4;
char data[MAX_BLOCK_SIZE];
FILE *fp;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "r");
if(fp == NULL){
printf("File not found\n");
exit(1);
}
do{
fscanf(fp, "%s", buf);
nw = write(sd, buf, MAX_BLOCK_SIZE);
} while(fscanf(fp, "%s", buf) != EOF);
}
client.c receiving part:
else if(getCommand){
FILE *fp;
write(sd, buf, MAX_BLOCK_SIZE);
read(sd, buf, MAX_BLOCK_SIZE);
fp = fopen("receive.txt", "w");
if(fp == NULL){
printf("File could not be opened.\n");
exit(1);
}
fprintf(fp, "%s", buf);
}
As correctly reported in comments section, the core of your issue consists in fscanf() being called both for reading data and for detecting EOF in order to quit the loop. But the latter actually consume data as well!
If I execute your program passing the following input file
Yesterday
All my troubles seemed so far away
Now it looks as though they're here to stay
and printing the output to stdout with printf("%s\n", buf); (instead of pushing data to a socket), that's what I get:
Yesterday
my
seemed
far
Now
looks
though
here
stay
So I get not only the first word, but one every two words.
Furthermore you write to the socket MAX_BLOCK_SIZE bytes whatever is the number of valid bytes you correctly read from file, so buf will contain MAX_BLOCK_SIZE - strlen(buf) bytes of garbage.
This sending loop will fix most of the issues mentioned above:
while(fscanf(fp, "%s", buf) != EOF)
{
write(sd, buf, strlen(buf));
}
Please note how you still would have something to care about:
are we sure that all words fit a MAX_BLOCK_SIZE big buffer? A word logger than the block will cause out of bound access (and undefined behavior), so a way to limit the number of acquired characters should be implemented
are we sure that all the data is sent in one shot? Probably yes, as the words are relatively short, but generally speaking the return value of the write() function should be checked, and if it is different from the amount of data to be sent the code should manage a way to send the remainder (or even exit the loop if the return value is negative!)
I'm not going to address these issues in this answer, but it was correct mentioning them. What I would like to emphatize, istead, is that sending data in this way will generate an output without whitespaces_. Something like:
YesterdayAllmytroublesseemedsofarawayNowitlooksasthoughthey'reheretostay
Even assuming that you need just a text file transfer, probably it is not what you want. For this reason I suggest implementing a binary trasfer using fread() in this way
#include<stdio.h>
#include <stdlib.h>
#define MAX_BLOCK_SIZE 1024
char buf[MAX_BLOCK_SIZE];
int main(void)
{
char tmp[] = "srcFile.txt";
FILE * fp;
int rd;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "rb");
if(fp == NULL)
{
printf("File not found\n");
exit(1);
}
while(( rd = fread(buf, 1, MAX_BLOCK_SIZE, fp)) > 0)
{
write(sd, buf, rd);
//printf("%s\n", buf);
}
// You can call feof() or ferror() in order to discover if either EOF or an error occurred
return 0;
}
Uncommenting the printf what I get is exactly the input file. Please note how it would not necessary, on Linux environment, to select the binary mode with b, as
The mode string can also include the letter 'b' either as a last character or as a character between the characters in any of the two-character strings described above. This is strictly for compatibility with C89 and has no effect; the 'b' is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding the 'b' may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-UNIX environments.)
(the emphasis is mine).
Just some notes about the receiver part:
I cannot comment the initial write(sd, buf, MAX_BLOCK_SIZE); because I'm not sure about the detais of your application, but it look strange. Make sure to send consistent data, in any case.
Always check the return values of read and write! Not only they can fail (and the error, that you can get querying errno, must be properly managed) but they can also read/write from/to socket less bytes than requested
In the general case of a binary file, writing data to the output file with fprintf(fp, "%s", buf); is not safe, as buf could contain bytes that are not printable or, even worse, it won't be NULL-terminated. Be aware that binary data could contain the '\0' character inside it, and that would result in an unexpected truncation. Use fwrite(), instead.
I want to read a name from a file (for example config_file.txt with only one entry like run)
and then create filenames with that, like run0.txt, run1.txt and so on.
But I get something like run..0.txt with two black dots.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCHAR 1000
void generate(char const *fileName);
int main(int argc, char** argv) {
generate("config_file.txt");
}
void generate(char const *fileName) {
char id[MAXCHAR];
FILE *fp;
char str[MAXCHAR];
fp = fopen(fileName, "r");
if (fp == NULL) {
printf("Could not open file %s", fileName);
return 1;
}
while (fgets(str, MAXCHAR, fp) != NULL) {
strcpy(id, str);
}
fclose(fp);
FILE *filePtr;
char filename[100];
for(int i = 0;i < 8;i++){
sprintf(filename, "%s%d.txt", id,i);
filePtr = fopen(filename, "w");
}
fclose(filePtr);
}
As I noted in the comments, your code does not zap the newline that fgets() normally preserves as it reads lines before trying to add the extension to it.
The simple and reliable method for zapping the newline is:
str[stcspn(str, "\n")] = '\0';
There are alternatives that might be more efficient (though efficiency is probably a red herring here — creating files takes a lot longer than reading through a short line of characters), but you have to get a variety of conditions right (empty buffer, buffer with no newline, etc).
You also have:
if (fp == NULL) {
printf("Could not open file %s", fileName);
return 1;
}
You should report errors on stderr instead of stdout.
You might consider including the error number and/or error message.
You should finish the message with a newline.
You can't write return 1; in a function returning void — the compiler must complain about that.
C11 §6.8.6.4 The return statement:
¶1 A return statement with an expression shall not appear in a function whose return type is void. A return statement without an expression shall only appear in a function whose return type is void.
Hence, you should consider writing:
if (fp == NULL)
{
fprintf(stderr, "Could not open file %s for reading: %s\n", fileName, strerror(errno));
return;
}
I normally use a set of error reporting functions that I wrote, one of which tells the library the name of the program (err_setarg0(argv[0]); in main()) and the others of which produce error messages as desired. This code is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory.
I'd write:
if (fp == NULL)
err_syserr("failed to open file '%s' for reading: ", fileName);
The function doesn't return. If I wanted to return, I'd use err_sysrem() ('remark') and arrange a return. The sys part of the name means that the error number and message are automatically reported too. I prefer these to perror() because perror() doesn't make it easy to get the program name etc into the error message.
There are analogous libraries available on some systems — err(3) on macOS, and also available on Linux (err(3), does roughly the same job.
I'm working on a project, and I can't seem to figure out why a piece of my function for finding prime numbers won't run. Essentially, I want to code to first check the text file log for any previously encountered prime numbers, but no matter what I put for the while-loop containing fscanf(), it seems like my code never enters it.
int filePrime(int a) {
int hold = 0;
FILE *fp = fopen("primes.txt", "a+");
if (fp == NULL) {
printf("Error while opening file.");
exit(2);
}
/*
the while loop below this block is the one with the issue.
on first run, it should skip this loop entirely, and proceed
to finding prime numbers the old-fashioned way, while populating the file.
instead, it is skipping this loop and proceeding right into generating a
new set of prime numbers and writing them to the file, even if the previous
numbers are already in the file
*/
while (fscanf(fp, "%d", &hold) == 1){
printf("Inside scan loop.");
if (hold >= a) {
fclose(fp);
return 1;
}
if (a % hold == 0) {
fclose(fp);
return 0;
}
}
printf("Between scan and print.\n");
for (; hold <= a; hold++) {
if (isPrime(hold) == 1) {
printf("Printing %d to file\n", hold);
fprintf(fp, "%d\n", hold);
if (hold == a)
return 1;
}
}
fclose(fp);
return 0;
}
I have tried all sorts of changes to the while-loop test.
Ex. != 0, != EOF, cutting off the == 1 entirely.
I just can't seem to get my code to enter the loop using fscanf.
Any help is very much appreciated, thank you so much for your time.
In a comment, I asked where the "a+" mode leaves the current position?
On Mac OS X 10.11.4, using "a+" mode opens the file and positions the read/write position at the end of file.
Demo code (aplus.c):
#include <stdio.h>
int main(void)
{
const char source[] = "aplus.c";
FILE *fp = fopen(source, "a+");
if (fp == NULL)
{
fprintf(stderr, "Failed to open file %s\n", source);
}
else
{
int n;
char buffer[128];
fseek(fp, 0L, SEEK_SET);
while ((n = fscanf(fp, "%127s", buffer)) == 1)
printf("[%s]\n", buffer);
printf("n = %d\n", n);
fclose(fp);
}
return(0);
}
Without the fseek(), the return value from n is -1 (EOF) immediately.
With the fseek(), the data (source code) can be read.
One thing slightly puzzles me: I can't find information in the POSIX fopen() specification (or in the C standard) which mentions the read/write position after opening a file with "a+" mode. It's clear that write operations will always be at the end, regardless of intervening uses of fseek().
POSIX stipulates that the call to open() shall use O_RDWR|O_CREAT|O_APPEND for "a+", and open() specifies:
The file offset used to mark the current position within the file shall be set to the beginning of the file.
However, as chux notes (thanks!), the C standard explicitly says:
Annex J Portability issues
J.3 Implementation-defined behaviour
J.3.12 Library functions
…
Whether the file position indicator of an append-mode stream is initially positioned at
the beginning or end of the file (7.21.3).
…
So the behaviour seen is permissible in the C standard.
The manual page on Mac OS X for fopen() says:
"a+" — Open for reading and writing. The file is created if it does not exist. The stream is positioned at the end of the file. Subsequent writes to the file will always end up at the then current end of file, irrespective of any intervening fseek(3) or similar.
This is allowed by Standard C; it isn't clear it is fully POSIX-compliant.
Okay so this is probably has an easy solution, but after a bit of searching and testing I remain confused.. :(
Here is a snippet of the code that I have written:
int main(int argc, char *argv[]){
int test;
test = copyTheFile("test.txt", "testdir");
if(test == 1)
printf("something went wrong");
if(test == 0)
printf("copydone");
return 0;
}
int copyTheFile(char *sourcePath, char *destinationPath){
FILE *fin = fopen(sourcePath, "r");
FILE *fout = fopen(destinationPath, "w");
if(fin != NULL && fout != NULL){
char buffer[10000];//change to real size using stat()
size_t read, write;
while((read = fread(buffer, 1, sizeof(buffer), fin)) > 0){
write = fwrite(buffer, 1, read, fout);
if(write != read)
return 1;
}//end of while
}// end of if
else{
printf("Something wrong getting the file\n");
return 0;}
if(fin != NULL)
fclose(fin);
if(fout != NULL)
fclose(fout);
return 0;
}
Some quick notes: I am very new to C, programming, and especially file I/O. I looked up the man pages of fopen, fread, and fwrite. After looking at some example code I came up with this. I was trying to just copy a simple text file, and then place it in the destination folder specified by destinationPath.
The folder I want to place the text file into is called testdir, and the file I want to copy is called test.txt.
The arguments I have attempted to use in the copyFile function are:
"test.txt" "testdir"
".../Desktop/project/test.txt" ".../Desktop/project/testdir"
"/Desktop/project/test.txt" "/Desktop/project/testdir"
I just get the print statement "Something wrong getting the file" with every attempt. I am thinking that it may be because 'testdir' is a folder not a file, but then how would I copy to a folder?
Sorry if this a really basic question, I am just having trouble so any advice would be awesome!
Also, if you wanted to be extra helpful, the "copyTheFile" function is supposed to copy the file regardless of format. So like if its a .jpg or something it should copy it. Let me know if any of you guys see a problem with it.
This is with ISO/POSIX/C89/C99 on Linux.
At the start, you'll want to include stdio.h to provide FILE and the I/O function declarations:
#include <stdio.h>
Aside from this, your program compiles and works properly for me. Unfortunately you can't copy to a directory without using stat() to detect if the destination is a directory, and if so, appending a file name before opening the file.
Some other minor suggestions:
A buffer with a power of two bytes such as 4096 is probably more efficient due to it lining up with filesystem and disk access patterns
Conventionally, C functions that return a status code use 0 for success and other values such as 1 for failure, so swapping your return values may be less confusing
When a standard library function such as fopen, fread or fwrite fails, it is a good idea to use perror(NULL); or perror("error prefix"); to report it, which may look something like:
$ ./a.out
...
error prefix: No such file or directory
if you are trying to write a new file in a directory, you should be giving the full path of the file to be written. in your case
"C:...\Desktop\project\testdir\testfile"