next up previous
Next: December lecture Up: November lecture summary Previous: C strings


November 29

Until this point you've used scanf and printf from <stdio.h>. This works well when you have one standard input and one standard output, and can be adapted to reading and/or writing from files where needed, using a unix shell's IO redirection.

$ prog <infile /* redirect stdin to infile */
$ prog >outfile /* redirect stdout to outfile */
$ prog <infile >outfile /* do both */
Inevitably you'll want more. You may want to read or write to more than one file, or perhaps put error messages somewhere different than other output.

We're assuming that the files you're interested in are streams of bytes to be interpreted as characters, broken into lines by an OS-specific end-of-line character(s) (in CSC209 you'll discuss binary files). You can declare a file pointer, and then open a file attached to that pointer for reading:

FILE *fp;

if ((fp = fopen("/path/to/my/file", "r")) == NULL) {
     /* file can't be opened --- do something */;
};
The r indicates that data will be read from this file, but not written to it. The value of fp should be tested, since it will be NULL if the file could not be opened. You may also open a file for writing with the argument w (clobbers previous instances of this file, if it exists, creates it otherwise), or appending with a (appends to the end of a file, if it exists, creates it otherwise). There options to combine reading with writing or appending, but that's too exciting for today's lecture.

You might also want to change which file an already-open stream is attached to. The file-pointers stdin, stdout, and stderr are open by default, and your program can redirect them to named files:

if (freopen("/path/to/myerror", "w", stderr) == NULL) {
    /* error, handle it */;
}
If this was successful, any output to stderr is sent to myerror.

Before doing anything useful with a file pointer, you should know how to close a file that's attached to a file pointer. This operation ensures that any data buffered for the file is flushed to it, and satisfies any OS constraints on how many files may be open simultaneously:

fclose (fp);

With file pointers, you can write output to specific streams. fprintf behaves like printf, except you specify which file is being written to (printf writes to stdout by default):

fprintf(fp, "The number is: %d\n", num);
fprintf(stderr, "The number is not: %d\n", num-1);
fprintf(stdout, "The number could be: %d\n", num * num);
The first statement writes to whatever stream is attached to fp, the second to standard error. The third is identical to printf (without the file-pointer argument).

Symmetrically, there is fscanf to read space-delimited strings from a file:

while(fscanf(fp, "%d", &i) != EOF) {
    /* do something with i */;
}
Since scanf returns the number of of items matched and assigned to variables, the loop above could have replaced != EOF with == 1.

If you want to read in lines, use fgets NOT gets:

char *buf[80+1];
while (fgets(buf, sizeof(buf), fp) != EOF) {
    /* process buf */;
}
This stores either the first sizeof(buf) - 1 characters, or up to (and including) the first newline from the stream attached to fp in buf.

You may need to get your input character-by-character (if you can't get what you want from scanf or fscanf):

char ch;
while ((ch = getc(fp)) != EOF) {
    /* do something with ch */;
}
There is (nearly identically) fgetc, which has the advantage/disadvantage of being defined as a function (getc is usually a macro).

Here's a fragment of code to merge two files containing sorted lists of integers (represented as strings) (this may well be the starting point for either next Monday's tutorial or next Wednesday's tutorial):

    FILE *fp0, *fp1;
    int x, y;
    int eof0, eof1;

    /* open fp0 and fp1 for reading ... */

    eof0 = fscanf(fp0, "%d", &x);
    eof1 = fscanf(fp1, "%d", &y);
    while (eof0 != EOF && eof1 != EOF) {
        if (x < y) {
            printf("%d\n", x);
            eof0 = fscanf(fp0, "%d", &x);
        } else {
            printf("%d\n", y);
            eof1 = fscanf(fp1, "%d", &y);
        }
    }
    while (eof0 != EOF) {
        printf("%d\n", x);
        eof0 = fscanf(fp0, "%d", &x);
    }
    while (eof1 != EOF) {
        printf("%d\n", y);
        eof1 = fscanf(fp1, "%d", &y);
    }


next up previous
Next: December lecture Up: November lecture summary Previous: C strings
Danny Heap 2002-12-16