I'm counting the number of lines in a file using feof as you see below. After that finishes, I need to loop line by line through the same file and do another operation that depends on knowing the number of lines. However, while (!feof(f)) won't run a second time on the same file stream. Is there a way to reset the f back to the beginning so I can loop through it again?
while (!feof(f))
{
ch = fgetc(f);
if(ch == '\n')
{
lines++;
}
}
while (!feof(f))
{
//need to do an operation that depends on knowing number of lines from first feof but this loop doesn't run because f is at the end
}
The call you seek is fseek.
Specifically this will set the file position back to the start of the file:
fseek(f, 0, SEEK_SET);
Note that this can fail (for instance if the file isn't a normal file but a socket or some such) so you'd want to check its return value and, if it returns -1, errno.
Related
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.
for example I have test.txt with
test1
test2
at the end of each line I would like to add some text
my code is:
fp = fopen("test.txt", "w");
while ((ch=getc(fp)) != EOF)
if (ch == '\n')
fprintf(fp, "newText");
my output is:
test1
newText
The outcome should be:
test1newText
test2newText
You cannot do it with a single file.
Here is what you need to do:
Open an auxiliary file for temporary output
Read the actual file line by line
For each line write the copy of the line into the auxiliary output
After writing the line write the suffix that you would like to append
Go to the next line until you process the whole file
Once you finished processing the whole file, close both files, and move the auxiliary file in place of the original input.
How to avoid a new line?, I successfully copy [the text] but when I try to add some text at the end of each line, it starts from new line
This is because you include the end-of-line character in your copied text.
If you read character-by-character, do not copy '\n's when you see them; if you read with fgets, check the last character, and skip it if it happens to be '\n' (fgets includes it in its return value).
You can’t read from your output file like that. It is possible to open a file for reading and writing, but it’s an advanced topic and isn’t really appropriate for text files.
The normal approach is to write to another file, and then replace the original if desired. Note that you’ll have to emit all the characters you read and your extra text.
If this is the whole function of the program, consider making it a filter (by using the standard streams); this is what’s expected by experienced users and saves some error checking.
eventually, I have succeed
if (!strcmp(fileName, SWM_DEBUG))
{
fp = fopen(fileName, "r");
fpOut = fopen("tmp.txt", "w");
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
fprintf(fpOut, "TEST");
fputc(ch, fpOut);
}
}
Thanks for your support ☺
You need to change the fopen call to read from the file and use the fputc function to re-output the character you read, like this:
fp = fopen("test.txt", "r");
while ((ch=fgetc(stdin)) != EOF) {
if (ch == '\n') {
fprintf(fp, "newText");
}
fputc(ch, fp);
}
fprintf(fp, "newText");
As pointed out elsewhere, you can't simultaneously read and write a file in the way you were trying to do, so i've changed it to read from stdin to illustrate the code
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.
Does feof() checks for eof for the current position of filepointer or checks for the position next to current filepointer?
Thanks for your help !
Every FILE stream has an internal flag that indicates whether the caller has tried to read past the end of the file already. feof returns that flag. The flag does not indicate whether the current file position is as the end of the file, only whether a previous read has tried to read past the end of the file.
As an example, let's walk through what happens, when reading through a file containing two bytes.
f = fopen(filename, "r"); // file is opened
assert(!feof(f)); // eof flag is not set
c1 = getc(f); // read first byte, one byte remaining
assert(!feof(f)); // eof flag is not set
c2 = getc(f); // read second byte, no bytes remaining
assert(!feof(f)); // eof flag is not set
c3 = getc(f); // try to read past end of the file
assert(feof(f)); // now, eof flag is set
This is why the following is the wrong way to use eof when reading through a file:
f = fopen(filename, "r");
while (!feof(f)) {
c = getc(f);
putchar(c);
}
Because of the way feof works, the end-of-file flag is only set once getc
tries to read past the end of the file. getc will then return EOF, which is
not a character, and the loop construction causes putchar to try to write it
out, resulting in an error or garbage output.
Every C standard library input method returns an indication of success or
failure: getc returns the special value EOF if it tried to read past the
end of the file, or if there was an error while reading. The special value is
the same for end-of-file and error, and this is where the proper way to use
feof comes in: you can use it to distinguish between end-of-file and error
situations.
f = fopen(filename, "r");
c = getc(f);
if (c == EOF) {
if (feof(f))
printf("it was end-of-file\n");
else
printf("it was error\n");
}
There is another internal flag for FILE objects for error situations:
ferror. It is often clearer to test for errors instead of "not end of file".
An idiomatic way to read through a file in C is like this:
f = fopen(filename, "r");
while ((c = getc(f)) != EOF) {
putchar(c);
}
if (ferror(f)) {
perror(filename):
exit(EXIT_FAILURE);
}
fclose(f);
(Some error checking has been elided from examples here, for brevity.)
The feof function is fairly rarely useful.
You can get a better understanding of how feof works, by knowing how it's implemented. Here is a simplified version of how the 7th Edition Unix stdio library implements feof. Modern libraries are very similar, adding code offering thread-safety, increased efficiency, and a cleaner implementation.
extern struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
char _flag;
char _file;
} _iob[_NFILE];
#define _IOEOF 020
#define feof(p) (((p)->_flag&_IOEOF)!=0)
#define getc(p) (--(p)->_cnt>=0? *(p)->_ptr++&0377:_filbuf(p))
int
_filbuf(FILE *iop)
{
iop->_ptr = iop->_base;
iop->_cnt = read(fileno(iop), iop->_ptr, BUFSIZ);
if (iop->_cnt == 0) {
iop->_flag |= _IOEOF;
return(EOF);
}
return(*iop->_ptr++ & 0377);
}
The stdio library maintains with each file a structure containing an internal buffer pointed by _base. The current character in the buffer is pointed by _ptr and the number of characters available is contained in _cnt. The getc macro, which is the base for a lot of higher-level functionality, like scanf, tries to return a character from the buffer. If the buffer is empty, it will call _filbuf to fill it. _filbuf in turn will call read. If read returns 0, which means that no more data is available, _filbuf will set the _IOEOF flag, which feof checks each time you call it to return true.
As you can understand from the above, feof will return true the first time you try to read a character past the end of the file (or a library function tries in your behalf). This has subtle implications on the behavior of various functions. Consider a file containing a single character: the digit 1. After you read that character with getc, feof will return false, because the _IOEOF flag is unset; nobody has yet tried to read past the end of the file. Calling getc again will result in a call to read, the setting of the _IOEOF flag, and this will cause feof to return true. However, after reading the number from the same file using fscanf("%d", &n), feof will immediately return true, because fscanf will have tried to read additional digits of the integer.
I am reading a binary file byte-by-byte,i need determine that whether or not eof has reached.
feof() doesn't works as "eof is set only when a read request for non-existent byte is made". So, I can have my custom check_eof like:
if ( fread(&byte,sizeof(byte),1,fp) != 1) {
if(feof(fp))
return true;
}
return false;
But the problem is, in case when eof is not reached, my file pointer is moved a byte ahead.
So a solution might be to use ftell() and then fseek() to get it to correct position.
Another solution might be to buffer the byte ahead in some temporary storage.
Any better solutions?
If you're reading a byte at a time, the idiomatic way to do this is with fgetc:
int c;
while ((c = fgetc(fp)) != EOF) {
// Do something.
}
and then you shouldn't need to deal with feof.
I typically do something like this:
int get_next_char(FILE* fp, char *ch)
{
return fread(ch, sizeof(char),1, fp) == 1;
}
// main loop
char ch;
while (get_next_char(fp, &ch))
process_char(ch);
if (!feof(fp))
handle_unexpected_input_error(fp);
It's best to structure your code so that you try to read some data and if the read does not succeed due to reaching the end-of-file, you deal with it there (i.e. see Kristopher Johnson's answer).
If you absolutely hate this, you can use ungetc to return a single character back to the stream and it will be available in the next read call:
int c = fgetc(fp);
if (c == EOF)
{
// handle eof / error
}
else
{
ungetc(c, fp);
// the next read call is guaranteed to return at least one byte
}
I'm not clear, but if you're interested in knowing before you read the byte if EOF has been reached, put your feof() test before the fread() rather than after it.
actually, if I read correctly, you don't even want to do the fread so:
return feof(fp) ? true : false;
I would suggest using:
fseek(myFile, 0, SEEK_END);
fileLen = ftell(myFile);
fseek(myfile, 0, SEEK_SET);
When you first open the file to determine the length. Then set up your read-loop to never read off the end. You can use ftell to figure out where you are, and compare to fileLen to figure out how much further you have to go.