Determining if a file being inputted and read is really empty - c

I have written this void function to check input files being read are valid, but there is one thing I don't know how to implement.
fseek and ftell are used to determine if a file is empty or not, but if a file is full of spaces or tabs, it will count as not being empty (though it is).
The input files will either contain integers and/or word strings.
So is there a way to check if the whole file has any ints or strings or otherwise only contains blank spaces?
I imagine I might have to use either scanf_s or gets, with isspace...
FILE *inp;
/* Checks to see if file can be read */
errCode = fopen_s(&inp, file_location, "r");
if (errCode != 0) {
printf(" ERROR. File %s was not located, ensure the following directory is valid: %s\n\n ", file_name, file_location);
*file_Error = ERROR;
return;
}
/*Move to end of file*/
fseek(inp, 0, SEEK_END);
/*Set filesize to size at end of file*/
fileSize = ftell(inp);
/*Print error and exit if filesize is zero i.e. contains 0 bytes (no data)*/
if (fileSize == 0) {
fclose(inp);
printf(" ERROR. File %s contains no data. (File size = %d bytes)\n\n ", file_name, fileSize);
*file_Error = ERROR;
return;
}
/*Print error and exit if filesize is zero for .dats containing only spaces or \t i.e. contains 0 bytes (no data)*/
/*Reset to beginning of file for reading*/
fseek(inp, 0, SEEK_SET);
/* Checks for error reading file occured */
if (errCode != 0) {
printf(" Error opening file %s.\n\n ", file_name);
*file_Error = ERROR;
return;
}
/*if no error on file*/
else
{
printf_s(" File %s Opened.\n\n", file_name);
*file_Error = 0;
}

Related

Write struct into file in c

I am trying to write struct to file. I have done the part where I defined struct I read text from file and then I saved it in struct now I am on a point where I want to make change in struct: students[i].name and then I want the change to be reflected (write) in file but it has invalid encoding.
file.txt has this structure:
U,Virat Kohli,Virat Kohli,
U,Serena Williams,Virat Kohli,
G,Wayne Gretzky,Virat Kohli,
U,Virat Kohli,Virat Kohli,
U,Serena Williams,Virat Kohli,
G,Wayne Gretzky,Virat Kohli,
Here is my code:
#include <stdio.h>
#include <string.h>
typedef struct
{
// members for the student's type, name, surname
char type;
char name[50];
char surname[50];
} Student;
int main(void)
{
// for comparing strcmp
int result;
// file pointer variable for accessing the file
FILE *file;
// attempt to open file.txt in read mode to read the file contents
file = fopen("file.txt", "r");
// if the file failed to open, exit with an error message and status
if (file == NULL)
{
printf("Error opening file.\n");
return 1;
}
// array of structs for storing the Student data from the file
Student students[100];
// read will be used to ensure each line/record is read correctly
int read = 0;
// records will keep track of the number of Student records read from the file
int records = 0;
// read all records from the file and store them into the students array
while (fscanf(file, " %c , %49[^,], %49[^,],", &students[records].type, students[records].name, students[records].surname) == 3)
{
// if fscanf read 3 values from the file then we've successfully read
records++;
// if there was an error reading from the file exit with an error message
// and status
if (ferror(file))
{
printf("Error reading file.\n");
return 1;
}
}
// close the file as we are done working with it
fclose(file);
// print out the number of records read
printf("\n%d records read.\n\n", records);
// print out each of the records that was read
for (int i = 0; i < records; i++)
printf("%c %s %s\n",
students[i].type,
students[i].name,
students[i].surname);
printf("\n");
// change first record's name to Elena Heis
for (int i = 0; i < 1; i++)
{
if (students[i].name == students[i].name)
{
printf("%s\n",
students[i].name);
strcpy(students[i].name, "Elena Heis");
printf("%s\n",
students[i].name);
}
}
// write changes to file
file = fopen("file.txt", "wb");
if (file != NULL)
{
fwrite(students, sizeof(Student), 1, file);
fclose(file);
}
return 0;
}
After write file has broken encoding like this
It should be
Your code is clean and your format string is almost perfect, yet parsing the csv file (or any file in general) with fscanf() is not recommended as it is very difficult to recover from errors and newlines are mostly indistinguishable from other white space characters. In particular, the \n at the end of the format string will match any possibly empty sequence of white space characters.
Testing ferror() and feof() as you do seems fine but insufficient to ensure reliable parsing: if fscanf() returns a short code, for example because of an empty field, parsing will continue from the middle of the offending line and neither ferror() nor feof() will cause the loop to end.
You should instead read one line at a time with fgets() and use sscanf() to parse the line.
Also note these remarks:
the csv file does not seem to contain name and surname fields but rather the full names of opponents.
this file seems to have trailing , after the third field. If this is expected, the format string ensuring record validity should be changed to "%c,%49[^,],%49[^,\n]%1[,\n]"
you should check that records < 100 to avoid a buffer overflow.
the test if (students[i].name == students[i].name) is useless and always true. No test is needed to change the name of the first student.
you cannot write the text file with fwrite(students, sizeof(Student), 1, file), you should instead use fprintf as for the output to the terminal.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
// members for the student's type, name, surname
char type;
char name[50];
char surname[50];
} Student;
int main(void)
{
// file pointer variable for accessing the file
FILE *file;
// attempt to open file.txt in read mode to read the file contents
file = fopen("file.txt", "r");
// if the file failed to open, exit with an error message and status
if (file == NULL)
{
fprintf(stderr, "Error opening file %s for reading: %s\n", "file.txt", strerror(errno));
return 1;
}
// array of structs for storing the Student data from the file
Student students[100];
// length of the students array
int nrecords = sizeof(students) / sizeof(*students);
// buffer to read one line at a time
char buffer[256];
// records will keep track of the number of Student records read from the file
int records = 0;
// read all records from the file and store them into the students array
while (records < nrecords && fgets(buffer, sizeof buffer, file))
{
// Read a line/record from the file
// if it was able to read successfully which we expect read will be 3
char newline[3]; // for characters at end of line: `,\n` or `\n`
char extra; // to ensure no more characters are present
// there are 5 conversion specifiers, but sscanf should parse
// exactly 4 fields, including the trailing `,` and the newline
int read = sscanf(buffer,
"%c,%49[^,],%49[^,\n]%2[,\n]%c",
&students[records].type,
students[records].name,
students[records].surname,
newline, &extra);
// if fscanf read 3 values and a newline from the file then we've successfully read
// in another record
if (read == 4) {
records++;
} else {
fprintf(stderr, "invalid record: %s", buffer);
}
}
// if there was an error reading from the file exit with an error message
// and status
if (ferror(file))
{
fprintf(stderr, "Error reading file: %s\n", strerror(errno));
fclose(file);
return 1;
}
// close the file as we are done working with it
fclose(file);
// print out the number of records read
printf("\n%d records read.\n\n", records);
// print out each of the records that was read
for (int i = 0; i < records; i++)
{
printf("%c %s %s\n",
students[i].type,
students[i].name,
students[i].surname);
}
printf("\n");
// change first record's name to Elena Heis
strcpy(students[0].name, "Elena Heis");
printf("%s\n", students[0].name);
// write changes to file
file = fopen("file.txt", "w");
if (file == NULL)
{
fprintf(stderr, "Error opening file %s for writing: %s\n", "file.txt", strerror(errno));
return 1;
}
// print out each of the records that was read
for (int i = 0; i < records; i++)
{
fprintf(file, "%c,%s,%s,\n",
students[i].type,
students[i].name,
students[i].surname);
}
fclose(file);
return 0;
}

fread into buffer is blank despite non-empty file

I'm trying to read in 256 bytes into a buffer from a 65536 byte file, treating it as a random-access file by using fopen, fread, fwrite, and fseek. I'm not getting any errors, but the buffer is still empty after the read, even though the file is non-empty and fread reports reading 256 bytes. Here's my code:
FILE *file = NULL;
char buffer[255];
memset(buffer, 0, sizeof(buffer));
file = fopen("BACKING_STORE.bin","r");
if(file == NULL) {
printf("Error: can't open file.\n");
return NULL;
} // end if
if(fread(buffer, 1, 256, file) != 256) {
printf("Error: reading from file failed.\n");
return NULL;
} // end if
printf("The bytes read are [%s]\n", buffer);
fclose(file);
And just to confirm, I opened up the BACKING_STORE.bin file in a hex editor just to make sure that it wasn't empty. Here's a screenshot of that:
After running this program, I get the output: "The bytes read are []" but without any errors.
I'm fairly new to C, so I'm sure it's just something simple I'm missing.
Thanks for the help. :)
Because you can't output binary data with "%s" and printf(). If you want to see the contents you can write a loop and print the hex value of each byte, like this
for (size_t i = 0 ; i < 256 ; ++i) {
fprintf(stdout, "0x%02x ", buffer[i]);
if ((i + 1) % 8 == 0) {
fputc('\n', stdout);
}
}

Reading from file into Structure in C

I'm new to programming in C, doing some work on a MIDI recording program that plays back the notes, and can't seem to get the program to read from the file into my structure array.
Here's the structure:
typedef struct
{
int noteNumber;
int vel;
int oscillatorNumber;
float freq;
} oneNote;
And here's the code to read the notes in:
oneNote notes[2000];
for (count = 0; count < fileSize; count++)
{
fscanf(filePointer, "%d %d %d\n", &notes[count].noteNumber,
&notes[count].vel,
&notes[count].oscillatorNumber);
notes[count].freq = ntof(notes[count].noteNumber);
}
Code where file is opened:
filePointer = fopen("noteRecordFile.txt", "r");
if (filePointer == NULL)
{
printf("Error opening file\n");
}
else
{
printf("File opened\n");
fseek(filePointer, 0L, SEEK_END);
fileSize = ftell(filePointer);
}
Just doesn't store the and of the data in the structure, as can be seen here:
Image of debug console
First few lines of noteRecordFile.txt:
48 108 0
50 108 0
52 100 0
There are several issues:
Remove following 2 lines, because it puts the file pointer to the end of the file and we want to start the reading at the start of the file, and ftell will give you the number of bytes in the file and not the number of lines.
fseek(filePointer, 0L, SEEK_END);
fileSize = ftell(filePointer);
Then you need this:
FILE *filePointer = fopen("noteRecordFile.txt", "r");
if (filePointer == NULL)
{
printf("Error opening file\n");
exit(1); // <<< abort program if file could not be opened
}
else
{
printf("File opened\n");
}
int count = 0;
do
{
fscanf(filePointer, "%d %d %d", &notes[count].noteNumber,
&notes[count].vel,
&notes[count].oscillatorNumber);
notes[count].freq = ntof(notes[count].noteNumber);
count++;
}
while (!feof(filePointer)); // <<< read until end of file is reached
...
We cannot know the number of lines the file contains without reading the whole file, so we use a different approach: we just read until the end of the file is reached.
You still need to add a check, because if the file contains more than 2000 lines, you will run into trouble. This is left as an exercise to the reader.
It won't because you reached the end of the file on the line:
fseek(filePointer, 0L, SEEK_END);
you need to reset the file pointer to the beginning of the file:
fseek(filePointer, 0L, SEEK_SET)
Are you sure about your file format ?
As i see, you read the header too as a normal data line...
Try reading this, maybe it'll help you.
MIDI
You can try openning the file as binary, i remember it fixed an issue i had on some sound files...!
Do you have any error/warning during compilation and execution ?

How can I determine if a file is empty?

How do I determine if a file is empty? The file is opened by a C program running on the Windows platform. I want to open a file in append mode, and, if empty, first print a header to it.
// Open CSV & write header
report_csv = fopen("SNR.csv", "a+");
if (!report_csv) {
fprintf(stderr, "Unable to open CSV output file...");
return -1;
}
if (!ftell(report_csv)) {
fprintf(report_csv, "Column A;Column B;Column C\n");
}
// ... print data to file
fclose(report_csv);
I was expecting ftell to return the current file size if the file was not empty, which happens because the code above is looped.
However, ftell always returns 0 and the header is printed multiple times.
I know I could fopen it with r and use fseek/ftell/fclose and then fopen it again with a+, but I think it's possible to do this without opening and closing the file multiple times.
Actually, when fopening a file in append mode, the file pointer is initially at the begining of the file. It moves to the end of it as soon as you write something or use fseek.
I just needed to add fseek(report_csv, 0, SEEK_END); before my if (!ftell(report_csv)).
Let's check this.
Code:
#include <stdio.h>
int main(int argc, char **argv) {
FILE *test;
size_t size;
char buf[100];
/* Truncate file */
test = fopen("test", "w");
if (!test) {
fprintf(stderr, "Cannot open file `test`!\n");
return 1;
}
/* Write something */
fprintf(test, "Something. ");
fclose(test);
/* Open in append */
test = fopen("test", "a+");
if (!test) {
fprintf(stderr, "Cannot open `test` in append mode!\n");
return 1;
}
/* Try to get the file size */
size = ftell(test);
printf("File pointer is: %d\n", size);
fseek(test, 0, SEEK_END);
size = ftell(test);
printf("After `fseek(test, 0, SEEK_END)`, the file pointer is: %d\n", size);
/* Append */
fprintf(test, "And that. ");
fclose(test);
/* Same without fseek */
test = fopen("test", "a+");
if (!test) {
fprintf(stderr, "Cannot open `test` in append mode!\n");
return 1;
}
fprintf(test, "Hello! ");
size = ftell(test);
printf("File size is now: %d\n", size);
fclose(test);
/* Try to read */
test = fopen("test", "r");
if (!test) {
fprintf(stderr, "Unable to open `test` for reading!\n");
return 1;
}
printf("File contents:\n\t");
while (test && !feof(test)) {
fgets(buf, sizeof(buf), test);
printf("%s", buf);
}
/* Cleanup & exit */
fclose(test);
printf("\n\nExiting.\n");
return 0;
}
Output:
File pointer is: 0
After `fseek(test, 0, SEEK_END)`, the file pointer is: 11
File size is now: 28
File contents:
Something. And that. Hello!
Exiting.
When opening a file with fopen with the a+ mode, all writing operations will be performed at the end of the file. You can reposition the internal pointer to anywhere in the file for reading, but writing operations will move it back to the end of file. The initial pointer position for reading is at the beginning of the file.
So you need to call an fseek(pFile, 0, SEEK_END) on your FILE pointer.
You can call _stat() and get the value st_size into struct _stat (you dont need open the file).Declared in sys/types.h followed by sys/stat.h
I donĀ“t know Windows programming, but it can help you: http://msdn.microsoft.com/en-us/library/14h5k7ff.aspx

Looking for a string of characters copied to a buffer

I have an assignment that has asked me to copy a file using buffered i/o. It has multiple requirements:
Take one parameter and an optional second
Open the first parameter for reading
Open the second for writing
If there is no second parameter make a new file called prog1.out
Use a buffer size of 20 bytes
When copying the file, print any buffer starting with the characters "rwxr"
close all opened files before exiting.
The problem I'm having is with number six, I've looked around and can't figure this out. I've tried memchr but I don't think I'm on the right track. If anyone can help push me in the right direction I'd be grateful.
This is my code:
# include <stdlib.h>
# include <stdio.h>
int main(int argc, char *argv[])
{
FILE *readfile, *writefile;
char buffer[1024];
int fileSize;
int readResult;
int writeResult;
// making sure arguments exist
if (argc < 2|| argc > 3){
printf("This program takes either 1 or 2 arguments.\n");
exit(1);
}
//Opening file for reading
readfile = fopen(argv[1], "r");
if (!readfile) {
printf("Unable to open file %s.\n", argv[1]);
exit(1);
}
//finding the file size
fseek (readfile, 0, SEEK_END);
fileSize = ftell (readfile);
fseek (readfile, 0, SEEK_SET);
// read the file
readResult = fread(buffer, 20, fileSize/20, readfile);
if (readResult == 0) {
printf("A read error occured.\n");
exit(1);
}
//check to see if there is a second parameter (argument)
if (argc == 3) {
writefile = fopen(argv[2], "w");
if (!writefile) {
printf("Unable to open file %s.\n", argv[2]);
exit(1);
}
writeResult = fwrite(buffer, 20, fileSize/20, writefile);
if (writeResult != readResult) {
printf("A write error occured.\n");
exit(1);
}
printf("File %s successfully copied to %s.\n", argv[1], argv[2]);
}
else {
writefile = fopen("program1.out", "w");
if (!writefile) {
printf("Unable to open file program1.out\n");
exit(1);
}
writeResult = fwrite(buffer, 20, fileSize/20, writefile);
if (writeResult != readResult) {
printf("A write error occured.\n");
exit(1);
}
printf("File %s successfully copied to %s.\n", argv[1], "program1.out
}
fclose(readfile);
fclose(writefile);
exit(0);
}
There's the naive way:
if(buffer[0] == 'r' && buffer[1] == 'w'
&& buffer[2] == 'x' && buffer[3] == 'r') {
//do something
}
But take a look at strncmp(), which you can use for comparing parts of a string.
remember to first check if you have read at least 4 chars into the buffer. e.g. if the file is 21 bytes long, your 2. fread might only read 1 character, and you shouldn't compare against the other 3 chars in the buffer.
If you print out the buffer with e.g. printf or puts or any other function that expects a string, the buffer needs to end with a '\0' byte, otherwise the string functions doesn't know when to stop.
I'll first answer the question you actually asked: memcmp is a good way to compare two buffers. Some caveats:
You also have to make sure the size of the buffer is at least as large as the size of the target string
memcmp returns 0 if the two buffers match, which can be counter-intuitive.
So for example, if you wanted to see if a buffer is equal to the string "rwxw", you could write
if (readresult >= strlen("rwxw") && !memcmp(buffer, "rwxw", strlen("rwxw"))) {
// buffer match occurred!
}
Personally I would use a "#define" or const char to ensure that the three places where that string appear are actually the same string. For example:
#define MATCH_STRING "rwxw"
if (readresult >= strlen(MATCH_STRING) && !memcmp(buffer, MATCH_STRING, strlen(MATCH_STRING))) {
// buffer match occurred!
}
However there are a couple of other problems with your code. One is that you need a loop that continually reads from the input file and write from the output file until the input is exhausted. For example, along the lines of
while (true) {
readResult = fread(buffer, 20, 1, readfile);
if (readResult == 0) {
// end of file
break;
}
// put your check for the "rwxr" string here!
writeResult = fwrite(buffer, readResult, 1, writefile);
if (writeResult != readREsult) {
printf("error\n");
}
}
Finally, you have what might be called a "stylistic" bug. There are two cases in your program: a specified filename and a default filename. Those two cases share a lot of code in common, but you did a cut and paste. This makes the code harder to understand, and more prone to bugs if it's changed in the future. If you are cutting-and-pasting code you're doing something wrong! Consider instead something like this, which maximizes shared code paths:
char *outFileName;
if (argc == 3) {
outFileName = argv[2];
} else {
outFileName = "prog1.out";
}
writefile = fopen(outFileName, "w");
if (!writefile) {
printf("Unable to open file %s.\n", writeFileName);
exit(1);
}

Resources