16.8: EOF and Errors

When a function returns EOF (or, occasionally, 0 or NULL, as in the case of fread and fgets respectively), we commonly say that we have reached ``end of file,'' but it turns out that it's also possible that there's been some kind of I/O error. When you want to distinguish between end-of-file and error, you can do so with the feof and ferror functions. feof(fp) returns nonzero (that is, ``true'') if end-of-file has been reached on the stream fp, and ferror(fp) returns nonzero if there has been an error. Notice the past tense and passive voice: feof returns nonzero if end-of-file has been reached. It does not tell you that the next attempt to read from the stream will reach end-of-file, but rather that the previous attempt (by some other function) already did. (If you know Pascal, you may notice that the end-of-file detection situation in C is therefore quite different from Pascal.) Therefore, you would never write a loop like

	while(!feof(fp))
		fgets(line, max, fp);
Instead, check the return value of the input function directly:
	while(fgets(line, max, fp) != NULL)
With a very few possible exceptions, you don't use feof to detect end-of-file; you use feof or ferror to distinguish between end-of-file and error. (You can also use ferror to diagnose error conditions on output files.)

Since the end-of-file and error conditions tend to persist on a stream, it's sometimes necessary to clear (reset) them, which you can do with clearerr(FILE *fp).

What should your program do if it detects an I/O error? Certainly, it cannot continue as usual; usually, it will print an error message. The simplest error messages are of the form

	fp = fopen(filename, "r");
	if(fp == NULL)
		{
		fprintf(stderr, "can't open file\n");
		return;
		}
or
	while(fgets(line, max, fp) != NULL)
		{
		... process input ...
		}

	if(ferror(fp))
		fprintf(stderr, "error reading input\n");
or
	fprintf(ofp, "%d %d %d\n", a, b, c);
	if(ferror(ofp))
		fprintf(stderr, "output write error\n");
Error messages are much more useful, however, if they include a bit more information, such as the name of the file for which the operation is failing, and if possible why it is failing. For example, here is a more polite way to report that a file could not be opened:
	#include <stdio.h>	/* for fopen */
	#include <errno.h>	/* for errno */
	#include <string.h>	/* for strerror */

	fp = fopen(filename, "r");
	if(fp == NULL)
		{
		fprintf(stderr, "can't open %s for reading: %s\n",
					filename, strerror(errno));
		return;
		}
errno is a global variable, declared in <errno.h>, which may contain a numeric code indicating the reason for a recent system-related error such as inability to open a file. The strerror function takes an errno code and returns a human-readable string such as ``No such file'' or ``Permission denied''.

An even more useful error message, especially for a ``toolkit'' program intended to be used in conjunction with other programs, would include in the message text the name of the program reporting the error.


Read sequentially: prev next up top

This page by Steve Summit // Copyright 1996-1999 // mail feedback