2

I am reading from a text file, iterating with a while(!feof) loop, but whenever I use this condition the loop iterates an extra time.

I solved the problem with this 'patchy' code

while (stop == FALSE)
{
 ...

        terminator = fgetc(input);
        if (terminator == EOF)
            stop = TRUE;
        else
            fseek(input, -1, SEEK_CUR);
}

But it looks and feels very bad.

Quaker
  • 1,483
  • 3
  • 20
  • 36
  • 2
    possible duplicate of [Why is iostream::eof inside a loop condition considered wrong?](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) –  Aug 30 '13 at 14:15
  • 2
    probably 20th question about C IO in the last few days, probably we need a community wiki page for that – jev Aug 30 '13 at 14:32
  • @H2CO3: Technically not a duplicate for the iostream question, since this is specifically C and the other is C++. – Adrian McCarthy Aug 30 '13 at 16:21
  • @jev: There's always a rash of questions like this in late summer/early autumn, when the programming courses get started. A community wiki page might be a good idea. – Adrian McCarthy Aug 30 '13 at 16:22
  • @AdrianMcCarthy yes; the concept is identical, though. –  Aug 30 '13 at 16:56
  • The way you've written it is not the most common idiom (see the answers for that), but aside from that `while (stop == FALSE)` is better written as `while (!stop)`. (If you think `(stop == FALSE)` is better because it's more explicit, how do you feel about `((stop == FALSE) == TRUE)`?). The identifiers `TRUE` and `FALSE` are non-standard anyway. – Keith Thompson Aug 30 '13 at 21:22
  • @KeithThompson so does `BOOL` in C, but it's a simple typedef and two definitions which makes life alot easier... – Quaker Aug 31 '13 at 08:42
  • @Quaker: C has has `bool`, `false`, and `true` (defined in `` since 1999 -- unless you're stuck using Microsoft's compiler. My point is that, even with `true` and `false` (or `TRUE` and `FALSE`), `while (!stop)` is clearer. And comparing a value for equality to `TRUE`/`true` can actually fail, since any non-zero value is true, but only `1` is *equal* to `true`. If you already have a boolean value, just use it as a boolean value. – Keith Thompson Aug 31 '13 at 19:01
  • @KeithThompson You were actually correct with the Micro$oft compiler. The thing about `!stop` and the use of boolean value is something I never though of or read of and it's quite important for every programmer to know. Thanks! – Quaker Aug 31 '13 at 21:46

3 Answers3

7

You can take advantage of the fact that an assignment gets evaluated as the value being assigned, in this case to the character being read:

while((terminator = fgetc(input))!= EOF) {
    // ...
}
BlackBear
  • 22,411
  • 10
  • 48
  • 86
  • wouldn't it keep the cursor a char ahead in case `!= EOF`? – Quaker Aug 30 '13 at 14:10
  • @Quaker No, why would it? –  Aug 30 '13 at 14:11
  • Since fgetc advances the indicator one char at a time and if `terminator != EOF` then `terminator` will hold a relevant char and the indicator will be ahead of it. – Quaker Aug 30 '13 at 14:12
  • @Quaker What kind of "cursor"? And why do you care at all? you will have your freshly read character in `terminator`. –  Aug 30 '13 at 14:13
6

Here is an idiomatic example (source):

fp = fopen("datafile.txt", "r"); // error check this!

// this while-statement assigns into c, and then checks against EOF:

while((c = fgetc(fp)) != EOF) {
    /* ... */
}

fclose(fp);
Brian Cain
  • 14,403
  • 3
  • 50
  • 88
  • Thanks for posting this. The fantastic enthusiasm around `feof()` in questions here is ... tiring. – unwind Aug 30 '13 at 14:12
  • @unwind Isn't the other answer equally correct as well, by the way? –  Aug 30 '13 at 14:12
  • Brian, I am reading the text file line by line with a function that reads the entire line to a string. Wouldn't your solution result in a trimmed string (missing the first char of the line)? – Quaker Aug 30 '13 at 14:16
  • @Quaker Again, no. Why would it? –  Aug 30 '13 at 14:17
  • @H2CO3, `The internal file position indicator is then advanced to the next character.` Taken from http://www.cplusplus.com/reference/cstdio/fgetc/ – Quaker Aug 30 '13 at 14:18
  • @Quaker Well, then you need to think about that more. It isn't broken. –  Aug 30 '13 at 14:24
  • 1
    @Quaker, a little nitpick -- please don't cite C++ references in a question tagged 'C'. – Brian Cain Aug 30 '13 at 14:25
  • Thinking about it more gets me back to where I began with ` if (terminator == EOF) stop = TRUE;` If this is the elegant way to solve it than the answer should have been `There is no better way`... – Quaker Aug 30 '13 at 14:25
  • @Quaker Well, the solution presented in these answers is definitely a better way. –  Aug 30 '13 at 14:26
0

Similarly you ca read line-by-line:

char buf[MAXLINE];
// ...
while((fgets(buf,MAXLINE,stdin)) != NULL) {
       do_something(buf);
}

Since fgets copies the detected newline character, you can detect end of line by checking the second to last buffer element. You can use realloc to resize the buffer (be sure you keep a pointer to the beginning of the buffer, but pass buf+n, to the next fgets, where n is the number of read characters). From the standard regarding fgets:

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first. A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.

Alternatively, you could read the whole file in one go using fread() (see example following the link).

jev
  • 2,023
  • 1
  • 17
  • 26