1

I'm reading a book about c programming and don't understand a shown example. Or more precisely I don't understand why it works because I would think it shouldn't.

The code is simple, it reads the content of a text file and outputs it in output area. As far as I understand it, I would think that the

 ch = fgetc(stream);

ought to be inside the while loop, because it only reads one int a time? and needs to read the next int after the current one has been outputted. Well, it turns out that this code indeed works fine so I hope someone could explain my fallacy to me. Thanks!

#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *stream;
    char filename[67];
    int ch;
    printf("Please enter the filename?\n");
    gets(filename);
    if((stream = fopen(filename, "r")) == NULL) {
        printf("Error opening the file\n");
        exit(1);
    }

    ch = fgetc(stream);
    while (!feof(stream)) {
        putchar(ch);
        ch = fgetc(stream);
    }

    fclose(stream);
}
Linus
  • 4,643
  • 8
  • 49
  • 74
  • This is not good quality code. As you can see in answers, you can avoid `feof` at all. Still, using `feof` there is no need to use two `fgetc`: it whould be enough `while (!feof(stream)) putchar(fgetc(stream));` – LS_ᴅᴇᴠ Jul 15 '13 at 08:34
  • Actually, this code will fail outputing one byte streams!!! – LS_ᴅᴇᴠ Jul 15 '13 at 08:36
  • Thanks a lot for all the answers, I wish I could accept all of them. – Linus Jul 15 '13 at 08:40

8 Answers8

2

If the fgetc outside while is removed, like this:

while (!feof(stream)) {
    putchar(ch);
    ch = fgetc(stream);
}

ch will be un-initialized the first time putchar(ch) is called.

By the way, don't use gets, because it may cause buffer overflow. Use fgets or gets_s instead. gets is removed in C11.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • @LS_dev No, when saying that `gets` may cause buffer overflow, it's because it has no idea of the size of the buffer. `fgets` doesn't have this problem, see http://c-faq.com/stdio/getsvsfgets.html – Yu Hao Jul 15 '13 at 08:49
2

I think you are confuse because of feof():

Doc: int feof ( FILE * stream );

Checks whether the end-of-File indicator associated with stream is set, returning a value different from zero if it is.

This indicator is generally set by a previous operation on the stream that attempted to read at or past the end-of-file.

   ch = fgetc(stream);      <---"Read current symbol from file"
    while (!feof(stream)) { <---"Check EOF read/returned by last fgetc() call"
        putchar(ch);        <---"Output lasts read symbol, that was not EOF"
        ch = fgetc(stream); <---"Read next symbols from file"
    }
   <-- control reach here when EOF found   

A much better way is to write your loop like:

while((ch = fgetc(stream))!= EOF){ <--" Read while EOF not found"
   putchar(ch);   <-- "inside loop print a symbol that is not EOF"
}

Additionally, Note: int fgetc ( FILE * stream );

Returns the character currently pointed by the internal file position indicator of the specified stream. The internal file position indicator is then advanced to the next character.

If the stream is at the end-of-file when called, the function returns EOF and sets the end-of-file indicator for the stream (feof).

If a read error occurs, the function returns EOF and sets the error indicator for the stream (ferror).

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • 1
    @LS_dev which code? the code I given `better way is??`: I am pretty sure it will not fail, atleast in normal circumstances for any size of file. – Grijesh Chauhan Jul 15 '13 at 08:36
  • Thank you very much for your effort in explaining everything, very helpful! – Linus Jul 15 '13 at 08:47
  • Correction, last character from ANY file will not be outputted! You read last character => pointer will be at end-of-file => !feof will be false => last character will not be outputted. – LS_ᴅᴇᴠ Jul 15 '13 at 08:49
  • @LinusAn Your welcome!, you should read [“while( !feof( file ) )” is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong?lq=1) Avoid using feof() – Grijesh Chauhan Jul 15 '13 at 08:51
  • Thank you for the link! While reading the details your answer again and again, a question crossed my mind: fgetc reads characters as well as numbers, right? Why is fgetc returning int and still able to read characters like 'a'? Thank you! – Linus Jul 15 '13 at 09:04
  • @LinusAn its very good question. it returns a int, because EOF file is int type. from file it read a char. Wait I give you a very good link – Grijesh Chauhan Jul 15 '13 at 09:06
  • 1
    @LinusAn Read: [Definition of EOF and how to use it effectively](http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?id=1043284351&answer=1048865140) – Grijesh Chauhan Jul 15 '13 at 09:08
  • 1
    Thanks for the link, very helpful! Wow, I didn't know how tricky that code is. – Linus Jul 15 '13 at 09:18
2

The code you have provided has 'ch =fgetc(stream);' before the While loop and also 'ch = fgetc(stream);' within the body of the loop. It would be logical that the statement within the loop is retrieving the ch from the stream one at a time as you correctly state.

DeeJayCee
  • 21
  • 3
1

It is inside and outside as you see. The one outside is responsible for reading the first character (which may be already the end of file, then the while wouldn't be entered anyway and nothing is printed), then it enters the loop, puts the character and reads the next one.. as long as the read character is not the end of file, the loop continues.

1

fgetc() reads a char(byte) and return that byte,The reading of byte value depends on where the read pointer is available.

Once fgetc() successfully read one byte the read file pointer moves to the next byte .so if you read the file the next byte will be the output and it will continue upto it find the end of the file where it return EOF.

pradipta
  • 1,718
  • 2
  • 13
  • 24
1

This is because of second fgetc which is getting call upto while (!feof(stream)).

Dayal rai
  • 6,548
  • 22
  • 29
1

Actually this part here:

while (!feof(stream)) {
        putchar(ch);
        ch = fgetc(stream);
    }

is pretty unsafe and you should avoid checking EOF like that (here why).

The way you should read a file using fgetc is like so:

int ch;
while ((ch = fgetc(stream)) != EOF) 
{
   printf("%c", ch)
}
Community
  • 1
  • 1
Nobilis
  • 7,310
  • 1
  • 33
  • 67
1

This is non functional code. Last character from file is never outputted. fgetc will read last character and pointer will be at end of file. So, when while is checked, !feof will return false, and read character will not be outputed. feofis not preventing reading after end of file: for empty files fgetc will be called before feof! Unless there is some benefit in console handling, two better options exist: Using feof:

while (!feof(stream)) {
    ch=fgetc(stream);
    putchar(ch);
}

Without using feof - because fgetc returns EOF when there are no more characters:

while ((ch=fgetc(stream))!=EOF) putchar(ch);
LS_ᴅᴇᴠ
  • 10,823
  • 1
  • 23
  • 46