2

Hello i had a simple copy file program in C but i cant explain why i get different output in the destination file when i use the 2nd method. The correct output with for loop:

I am the worst programmer in the world!
:D
 And this is bla bla bla bla
 more bla bla bla...

BUT with while loop a random char is generated in EOF:

I am the worst programmer in the world!
:D
 And this is bla bla bla bla
 more bla bla bla...


The code is

int main()
{
int i;
char ch;
create_files();
FILE *src = fopen("best.txt", "r");
FILE *dst = fopen("copied.txt", "w");
for(i=getc(src); i!=EOF; i=getc(src))  //correct copy
    {
        putc(i, dst);
    }

/* while(!feof(src))                  //woot?
    {
        ch=fgetc(src);
        fputc(ch,dst);
    }*/

fclose(dst);
fclose(src);
return 0;
}

void create_files()
{
    FILE *fp;
    fp = fopen("best.txt","w");
    fprintf(fp,"I am the worst programmer in the world!\n:D\n And this is bla bla bla bla\n more bla bla bla...\n");
    fclose(fp);
}

i ve used both fputc or putc and fgetc or getc and still the same. Did i forget something?

BugShotGG
  • 5,008
  • 8
  • 47
  • 63
  • 3
    [`feof()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/feof.html) does not do what you think it does. Read the documentation. It doesn't test if the next read will attempt to read past the end of file; it checks if the last **failed** read attempt failed because end-of-file had already been reached. – pmg Jan 13 '12 at 22:19
  • @pmg is right; `feof` should be used after a loop to check whether EOF occurred *as opposed to an error*. – Fred Foo Jan 13 '12 at 22:44

5 Answers5

2

What

while (!feof(src)) {
    ch=fgetc(src);
    fputc(ch,dst);
}

does, is:

  1. check for EOF
  2. read a character, possibly causing EOF
  3. output the character just read, without checking for EOF.

When EOF occurs, (3) is still executed before the check in (1) in the next iteration. The special value EOF is converted to a char and output.

The correct loop is

while ((ch = fgetc(src)) != EOF)
    fputc(ch, dst);

assuming you give ch the type int, because a char cannot represent EOF. Note the assignment within the check; some programmers will tell you that's ugly, but so many use it that you might as well get used to it. Your variant for loop is correct as well.

(Aside 1: fputc is equivalent to putc and fgetc to getc, as long as you don't try to use them in a function pointer context.)

(Aside 2: your while loop also doesn't check for stream errors, while checking for EOF returns catches that as well.)

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Thanks. I thought (!feof(src) is the same same as the ((ch = fgetc(src)) != EOF) – BugShotGG Jan 13 '12 at 22:29
  • @GeoPapas: no. You'd have to do the check *between* the read and the write, and then you'd also have to check for `ferror`. – Fred Foo Jan 13 '12 at 22:30
  • if i wanted to recopy the file after 1st copy its mandatory to use ungetc ? – BugShotGG Jan 13 '12 at 22:38
  • @GeoPapas: no. You should `fseek` to the beginning of the file. `ungetc` only guarantees *one* character can be pushed back; it's intended for implementing read-ahead in tokenizers. – Fred Foo Jan 13 '12 at 22:43
1

Your while loop has an off-by-one error. It reads/writes an extra character, which is where that bonus is coming from - you wrote the EOF value returned by fgetc() into the output file!

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
1

The first loop fetches the character (possibly detecting the EOF) and then checks the value didn't detect an EOF and possibly executes the block writing the character (until all characters have been read).

The second loop checks to see if an EOF was detected, fetches the character (possibly detecting an EOF), writes the character (without regard as to what it might be), and possibly continues onto the next character (if EOF wasn't detected).

In the second loop, you write the character before checking if it is a EOF.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
1

The while loop gives you a random character because EOF isn't actually flagged until a read fails. So what's happening in your while loop isyou do a read, fgetc fails, sets EOF, and returns a duff value back to you, which you then print.

A better way to structure the while loop would be:

ch=fgetc(src);
while (!feof(src)) {
   fputc(ch,dst);
   ch=fgetc(src);
}
Chris J
  • 30,688
  • 6
  • 69
  • 111
0

Edit

Looks like this isn't the actual problem, but I'll keep the answer as an advice.


The only thing I noticed is:

char ch;

Depending on your system, char can be signed or unsigned, enough to hold a EOF or not enough. Your while loop is probably demonstrating that your OS fits in the latter case.

Use int instead and give it a try.

sidyll
  • 57,726
  • 14
  • 108
  • 151