It is good practice to always close a file when finished reading or writing it. In the case of an input file, the close is simply a way of
saying "we're done." Note that a simple way to re-read a file is to close it and then re-open it, which starts the stream at the beginning.
However, since output files are normally buffered, it is important to close the file after the last write to ensure that all of the data gets
copied to the disk before the program terminates.
Writing and reading files
Once the file is open and you have a valid FILE pointer, writing and reading to/from the file couldn't be simpler; all you have to do is
to use the "file version" of printf and scanf, namely fprintf and fscanf, whose first argument is the FILE pointer, and are
otherwise identical in behavior:
int fprintf(FILE *, const char *, ...);
int fscanf(FILE *, const char *, ...);
It is useful to know that stdout and stdin are simply FILE * objects, and that printf and scanf are just short-hand ways to write
fprintf(stdout, and fscanf(stdin, - they are completely identical!
Using fprintf to write to a file is no problem. However, for file input, fscanf will return EOF if the attempt to process the request
specified in the format string results in an attempt to read past the end of the file. For example, suppose the next three characters in the
file are a space, the digit 3, and a newline, and there are no more characters in the file. Suppose we attempt a read of an integer. The
first space will be skipped, the 3 will be consumed and used as the value of the integer, and then the scan stops leaving the newline in
the stream. Suppose we then attempt to read another integer. The remaining newline will be skipped because it is whitespace, but end-
of-file is encountered before a digit is found. fscanf will then return EOF.
Key concept: The end-of-file condition is raised only if the function tries to read past the end of the data in the file. That is, EOF is not
caused by reading the last data item, only by trying to read data when there is no more to be read. For this reason, controlling a reading
loop by testing for end-of-file is only valid if no other kind of error condition could arise; it is better to control the loop by testing for a
successful read (e.g. scanf returns the correct value for all data items being read), and test for end-of-file only if necessary or useful
after a failed read. In fact, end-of-file is an error only if it is unexpected - because there is supposed to be more data, but for some
reason there isn't! Actually, detecting end-of-file is a common way to read an indefinite amount of data - simply read until there is no
more data. So here is the correct form of a reading loop for data in a file:
Attempt to read some input from the file.
Check for successful read.
! If success, and only if success,
! ! use the input (which may involve additional checks on validity).
! ! continue with the next read.
! If fail:
! ! If EOF is expected to mark the end of the data, and failure is due to EOF,
! ! ! then all data has been read; continue with the next step in the program.
! ! Otherwise, there is something wrong with the file and/or data; handle as appropriate:
! ! ! print an error message.
! ! ! terminate or give user the option to try something else.
Take special care with structuring your file reading loops - very confusing problems can result if there is bad data in the file and your
loop has been incorrectly structured.
Some additional handy functions for C stream I/O
int fgetc(FILE *); int getchar(void);
These functions both read and return the next character in the stream, regardless of what it is. If a character cannot be read, EOF is
returned. getchar() is identical as fgetc(stdin). They return an int rather than a char because technically, both a char and the
value used for EOF will fit into an int, while EOF might not fit into a char.
9