More About I/O







Now that we know about pointers and memory, we can start talking about more advanced kinds of I/O. First, I'll talk about file pointers, then moving onto the rest of the printf() family, and then dealing with more kinds of input.

File I/O

C defines a special kind of pointer in the stdio.h library (the same library where almost all of the I/O functions are defined, including all the functions mentioned in this tutorial). Called a file pointer, it allows you to read and write data from files. A file pointer is declared as the type FILE * (note the capitals). Once you have declared pointer, you must tell it what file it should point to. This is done via the fopen(char *filename, char *mode) function. It takes two arguments: the actual name of the file, and the mode. The mode can be one of three single-letter strings (not just characters). Use "r" if you want to open the file for reading only, "w" for writing, and "a" for appending (i.e., adding text to the end of the file and not overwriting what is already there). When you're finished using a file, it's a good idea to close it with the fclose(FILE *fptr) function. While this is not stricly necessary, it can avoid strange bugs, and is good style. As you read data from a file, the file pointer moves along through the file and keeps track of its current position. If you should need to reposition your place in the file, you can use the rewind(FILE *fptr) function to move the pointer back to the beginning of the file.

In addition to any files you open yourself, there are three preopened file pointers declared in every program. These are stdin, the standard input, stdout, the standard output, and stderr, the standard error reporting output. In general, using stdin and stdout will confine your operations to the terminal and the command line (on a UNIX system, that is).

Files are terminated by a special constant called EOF (note the capitals). This is the reason that the character-oriented input functions return integers rather than characters. EOF is a value outside the ASCII code, and so consequently could not be used in a character variable.

We'll see how to use a file you've opened in the next few sections.

The printf() Family

We've already seen the standard kind of printf(). But there are two other functions that are very similary which are also useful: sprintf() and fprintf(). sprintf() takes the same kind of arguments as the standard form except for an additional string argument (that goes first in the argument list). sprintf() will then put its output into this string instead of printing it to stdout. fprintf() also takes an additional first argument, but this time it should be a file pointer opened with either the "r" or "w" mode. fprintf() prints output to files. And, just for comparison's sake, the statement printf("%s", msg) is identical to fprintf(stdout, "%s", msg) . Everything else about the printf() family remains the same with these new functions.

The scanf() Family

You're probably wondering by now how to control input. One way to read in information is to use one of the scanf() functions. These are in many ways analagous to the printf() family we've already seen. In addition to the standard scanf() function, there are the two variants fscanf() and sscanf(). The arguments for these functions are quite similar to the printf() arguments. Except for the normal scanf(), where stdin is the default, the first argument should be either a string or a file pointer, depending on the function you're using. The second argument is the string to scan in. The format of this string is very important, since scanf() will look for exactly that sequence of characters. Also included in this string can be format codes just like those you use in printf(). There are a few new wrinkles, though. First of all, the variables specified in the list that comes after the control string must be pointers. So, if you want to scan a value into a normal variable, you should put the address-of operator (&) in front of it in the list. There are also a few additional format codes you can use. Use %[] to specify a set of characters that will be accepted. For example,

char ch;

scanf("%[abc]", &ch);

will read a character into ch if it is either a, b, or c. Remember that case is important. If instead you want to read in everything but some character, put a caret in front of the list. For example, to read in a line of input from stdin, you can use

char *str, termch;

scanf("%[^\n]%c", str, &termch);

The termch variable is there to remove the newline character from stdin, which would otherwise remain since you read in everything but it. Also note that I didn't put an ampersand in front of str since it was already a pointer.

In general, the scanf() family is difficult to deal with. I would recommend avoiding it when possible.

Various Other I/O Functions

You've already seen one other input function; recall getchar(). This and its more general equivalent getc(FILE *fptr) control character-oriented input. Each returns an integer rather than a character for reasons noted above. A common loop construction for reading all the characters from a file is

int ch;
FILE *infile;

while ((ch = getc(infile)) != EOF) {
   /* process the characters */
}

Since the code within the innermost parentheses gets executed first, getc() will assign the next character to the variable ch before checking its value against EOF.

getc() and getchar() have analogous output functions in putc() and putchar(). In addition, the function ungetc() can be used to push a single character back into the file (essentially backing the file pointer up one character).

For line-oriented input, you can use the functions gets(), fgets(), and puts(). The action of these functions is fairly straightforward, except that their arguments should be strings rather than characters.



Back to Strings   On to Binary Operations
Back to the Outline