C Programming. Using fopen fclose for text file operations - c

I am opening a text file and processing word count function to count words and closing a file.
Next, I open the same file again and store it in the array with limit to word count value in an array.
Here, if I use fopen and fclose just once like in line 1 and 16, my program does not work. But if I open it (line 1) process it then close it (line 10) and open it again (line 12) for second process, my program works. Does it mean that fopen can only handle one process at a time and I have to open it again for second process?
1. fptrr = fopen(fname,"r"); // open the text file in read mode
2.
3. if (fptrr == NULL) {//if file name does not match
4. printf("Error: No such file or directory");
5. return -1;
6. }
7.
8. wordCount += countWords(fptrr); //run word count function and get the value of total words
9.
10. fclose(fptrr); // close the file
11.
12. fptrr = fopen(fname,"r");
13. for(int i=0;i<wordCount;i++){ // define size of loop equal to words in a file
14. fscanf(fptrr, "%s", fileArray[i]); //scan and store in array
15. }
16. fclose(fptrr);

You can do whatever you want to the file while it is open.
I suspect your problem is that you are reading to the end of the file in one set of operations and then you try and read the file again while you are at the end. Look for the rewind() function
To rewind to the start of the file just call rewind(fptrr); after the first countwords. Alternately you can call fseek(fptrr, 0L, SEEK_SET) but rewind() is clearer.
Note that closing the file and re-opening it automatically resets the file to read form the start which is why your new version works.

Related

Can't rewrite the data at file starting but I can apped at the End in append mode in C

I want to store student information in a file in C, I choose to write in a binary file format.first I had written all student data to the file in write mode. first 4 bytes(int) represents the num of students and then subsequent data is for storing student Information.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode. Below is the code. Thanks in advance.
typedef struct
{
char* rollNo;
char* name;
char* grade;
}Student;
int numStudents;
void append(Student* newStudent){
FILE *fp;
if((fp=fopen("students.bin","ab"))==NULL){ f_err_report(ERR_OPEN);exit(1);}
//appending the new student at the end of the file
// writing rollNo
int len;
len = strlen(newStudent->rollNo)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->rollNo,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing name
len = strlen(newStudent->name)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->name,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing grade
len = strlen(newStudent]->grade)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->grade,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//updating the total number of students at the file starting. But this doesn't happen!!! It stays same as when I wrote in "write mode" previously
numStudents++;
rewind(fp);
if(fwrite(&numStudents,sizeof(int),1,fp)!=1){
f_err_report(ERR_WRITE);
}
printf("appended to the list \n");
fclose(fp);
}
When you called
rewind(fp);
you successfully reset the file pointer to the beginning of the file, ready to update the number of student records. All right so far.
But then you called
fwrite(&numStudents, sizeof(int), 1, fp)
to actually update the number of records. And since the file is opened in append mode, this translates implicitly to the equivalent of
fseek(fp, OL, SEEK_END);
fwrite(&numStudents, sizeof(int), 1, fp)
That is, in append mode, every time you write to the file, there's an automatic seek to the end of the file, followed by the write you asked for.
So your rewind() call accomplishes nothing in the long run, because it's undone by the next write.
Once upon a time, calling fopen with "a" mode basically just opened the file, and seeked to the end once. In those days, your code would have worked as you expected. But not any more. In your case, you want to use "w" mode (or "w+" or "r+", if you want to both read and write), and when you want to append a new record at the end, call
fseek(fp, OL, SEEK_END);
to explicitly get to the end when you need to.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode.
Yes there is:
Opening a file with append mode ('a' as the first character in the mode argument) causes all subsequent writes to the file to be forced to the then current end-of-file, regardless of intervening calls to the fseek function.
And from the Linux man page:
Opening a file in append mode (a as the first character of mode)
causes all subsequent write operations to this stream to occur at
end-of-file, as if preceded the call:
fseek(stream, 0, SEEK_END);
The only way on POSIX systems to open a file in append mode but still be able to write to any position in a file is to use the POSIX open() call with the O_APPEND flag and then use the POSIX pwrite() call to write to the desired location:
The pwrite() function shall be equivalent to write(), except that it writes into a given position and does not change the file offset (regardless of whether O_APPEND is set).
But pwrite() is broken on Linux:
POSIX requires that opening a file with the O_APPEND flag should
have no effect on the location at which pwrite() writes data.
However, on Linux, if a file is opened with O_APPEND, pwrite()
appends data to the end of the file, regardless of the value of
offset.

y with umlaut in file

I'm working on an example problem where I have to reverse the text in a text file using fseek() and ftell(). I was successful, but printing the same output to a file, I had some weird results.
The text file I input was the following:
redivider
racecar
kayak
civic
level
refer
These are all palindromes
The result in the command line works great. In the text file that I create however, I get the following:
ÿsemordnilap lla era esehTT
referr
levell
civicc
kayakk
racecarr
redivide
I am aware from the answer to this question says that this corresponds to the text file version of EOF in C. I'm just confused as to why the command line and text file outputs are different.
#include <stdio.h>
#include <stdlib.h>
/**********************************
This program is designed to read in a text file and then reverse the order
of the text.
The reversed text then gets output to a new file.
The new file is then opened and read.
**********************************/
int main()
{
//Open our files and check for NULL
FILE *fp = NULL;
fp = fopen("mainText.txt","r");
if (!fp)
return -1;
FILE *fnew = NULL;
fnew = fopen("reversedText.txt","w+");
if (!fnew)
return -2;
//Go to the end of the file so we can reverse it
int i = 1;
fseek(fp, 0, SEEK_END);
int endNum = ftell(fp);
while(i < endNum+1)
{
fseek(fp,-i,SEEK_END);
printf("%c",fgetc(fp));
fputc(fgetc(fp),fnew);
i++;
}
fclose(fp);
fclose(fnew);
fp = NULL;
fnew = NULL;
return 0;
}
No errors, I just want identical outputs.
The outputs are different because your loop reads two characters from fp per iteration.
For example, in the first iteration i is 1 and so fseek sets the current file position of fp just before the last byte:
...
These are all palindromes
^
Then printf("%c",fgetc(fp)); reads a byte (s) and prints it to the console. Having read the s, the file position is now
...
These are all palindromes
^
i.e. we're at the end of the file.
Then fputc(fgetc(fp),fnew); attempts to read another byte from fp. This fails and fgetc returns EOF (a negative value, usually -1) instead. However, your code is not prepared for this and blindly treats -1 as a character code. Converted to a byte, -1 corresponds to 255, which is the character code for ÿ in the ISO-8859-1 encoding. This byte is written to your file.
In the next iteration of the loop we seek back to the e:
...
These are all palindromes
^
Again the loop reads two characters: e is written to the console, and s is written to the file.
This continues backwards until we reach the beginning of the input file:
redivider
^
Yet again the loop reads two characters: r is written to the console, and e is written to the file.
This ends the loop. The end result is that your output file contains one character that doesn't exist (from the attempt to read past the end of the input file) and never sees the first character.
The fix is to only call fgetc once per loop:
while(i < endNum+1)
{
fseek(fp,-i,SEEK_END);
int c = fgetc(fp);
if (c == EOF) {
perror("error reading from mainText.txt");
exit(EXIT_FAILURE);
}
printf("%c", c);
fputc(c, fnew);
i++;
}
In addition to #melpomene correction about using only 1 fgetc() per loops, other issues exist.
fseek(questionable_offset)
fopen("mainText.txt","r"); opens the file in text mode and not binary mode. Thus the using fseek(various_values) as a valid offset into the file is prone to troubles. Usually not a problem in *nix systems.
I do not have a simple alternative.
ftell() return type
ftell() return long. Use long instead of int i, endNum. (Not a concern with small files)
Check return values
ftell() and fseek() can fail. Test for error returns.

Ansi C: Attempting to count total chars in a file

The task is simple but I am having an issue with the method returning 0.
This means my loop:
int getCharCount(FILE *fp) {
int c;
int i = 0;
while( (c = fgetc(fp)) != EOF) {
i++;
printf("Loop ran");
}
return i;
}
Did not run.
In my testing I found that the loop never runs because the "Loop ran" never prints. I am new to c and not sure if I am doing something wrong when trying to count chars in the file.
I feel like I should mention that the file is opened with "wb+" mode and that there are a few long methods that edit the file. Essentially before using this getCharCount() method the text file is cleared of all previous data, then user enters a number of 44 char length strings at a time and I use this method I just posted to calculate the total number of chars which will be used to navigate my display data method.
I am in a library working on this so if anything extra is needed to be posted or if anything needs to be clarified I will try to be quick with my responses. I don't want to post my whole code because there would be a chance to cheat and I need to get this done myself.
Thanks ahead.
If you write to the file and then call your method on the same file handle, the file handle is already at the end of the file so it will see EOF immediately. We would need to see more of the code to be sure I think.
So, you could rewind the file handle at the start of your function.
Or you could just call ftell to find out your offset in the file, which is the same as the number of bytes written if you truncate, write and do not rewind.
Why you have to read all bytes one by one to count them? It is much easier to do fseek( fp, 0, 2 ) to jump at end of file and get current position (file length) with ftell( fp ).
You are opening with the mode w+ which will truncate the file. From the fopen man page:
w+ Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
You will want to open it with rb+ instead of wb+ if you are reading from a file and still want to write to it. If you have already written to it and want to read what was written, you will need to seek to the start of the file pointer.
// Both of these seek to the start
rewind(fp);
fseek(fp, 0, SEEK_SET);
If the file is not open, you could use:
off_t getFileSize(const char *filepath) {
struct stat fileStat;
stat(filepath, &fileStat);
return(fileStat.st_size);
}
If the file is open:
off_t getFileSize(int fd) {
struct stat fileStat;
fstat(fd, &fileStat);
return(fileStat.st_size);
}

Random writing order to output file?

I've written a program that writes information to a file. The output to the file is not in the order that I am expecting. I have a header line and three additional lines with numerical information. The header comes first following by the third, first, and second lines.
Note that the file is open in a mode, not a+. According to various sources, re-positioning operators like fseek() are suppose to be ignored. For a while I was actually getting the third line before the first line after that line was written. If the fseek() line is omitted, the third line actually is written before the header ..
If the fseek() function is left commented (regardless of being in a or a+ mode) the output is as shown in the picture below.
I wrote in a bit of code to see how the output should be written. The text in the file is certainly not what it should be ..
I attempted to use the fseek() function to find the position just before the EOF before each write but to no avail.
I have also noticed that if I use fflush(writeP) then I get the same effect that including the fseek() function would. The file is still out of order as shown, but the third line is no longer before the header line.
What am I missing?
void quickSortHelper(int* num, long startTime, long endTime, int size){
FILE *writeP = fopen(QUICKSORT_FILE, "a");
if(writeP == NULL){
fputs("Error opening file\n", stderr);
exit(1);
}
static int times = 0;
long deltaT; //change in time
if(size < STEPSIZE){//first time, write header to file
printf("Writing header!\n");
fprintf(writeP, " --- QUICKSORT ---\nCOUNT\tTIME\tMEDIAN\n");
}
deltaT = (clock() - startTime)/(CLOCKS_PER_SEC/1000);
//fflush(writeP);
fseek(writeP, -1, SEEK_END);
printf("Writing: %d\t%ld\t%d\n", size, deltaT, findMedian(num, size));
fprintf(writeP, "%d\t%ld\t%d\n", size, deltaT, findMedian(num, size));
if(++times == 3)
fclose(writeP);
return;
}
With the fseek() line commented, the output is:
You do not close writeP the first 3 times when function is called. So, the file is opened by several FILE handles which get closed on exit. The "a" works only for the same FILE handle or when data have reached the disk.
The problem comes from the fact that you open the same file on every function call, but only close it on the third call. I would suggest moving the file opening and closing logic out of that function, and passing the FILE * handle as an argument to the function; this would also avoid having to hard code the call number on which to close into that function.
So the place where you call the function would look something like this:
FILE *writeP = fopen(QUICKSORT_FILE, "w"); // "a" changed to "w"
if (!writeP) {
perror(QUICKSORT_FILE);
exit(1);
}
// perhaps write the header into the file here
for (int i = 0; i < 3; ++i) {
// do the quicksort
write_quicksort_results(writeP, …);
}
(void) fclose(writeP);

Unexpected output when using fseek

Assuming we have a text file named hi.txt that contains the following string:
AbCdE12345
Let say we run this code:
int main() {
FILE *fp;
fp = fopen("hi.txt","r");
if(NULL == fp) { return 1; }
fseek(fp,-1, SEEK_END);
while (ftell(fp) > 0) {
printf("%c",fgetc(fp));
fseek(fp,-4, SEEK_CUR);
}
fclose(fp);
return 0;
}
When I ran this code it printed: 3EbCd
When I tried to guess what it would print I thought that it should be 52d.
Can anyone explain what has happened here ?
It looks like there is a non-printable end-of-line character at the end of your file. That's what gets printed first. Then the position is moved in turn to 3, E, and b. At this point, re-positioning by -3 fails, because the location would become -2. File cursor stays where it was, i.e. at C which gets printed next. The following attempt at repositioning fails too, so d gets printed. The next repositioning succeeds, terminating the loop.
To detect situations when fseek is ignored, check its return value, like this:
while (ftell(fp) > 0) {
printf("%c",fgetc(fp));
// Successful calls of fseek return zero
if (fseek(fp,-4, SEEK_CUR)) {
// Exit the loop if you can't jump back by 4 positions
break;
}
}
For files opened in text mode the offset passed to fseek is only meaningful for values returned by ftell. So the offset may not necessarily be in bytes. Try opening the file in binary mode:
fp = fopen("hi.txt", "rb");
and see if the results are different.

Resources