C68. C Character I/O


Character I/O
In this section, we'll examine library functions that read and write single charac­ters. These functions work equally well with text streams and binary streams.
You'll notice that the functions in this section treat characters as values of type int, not char. One reason is that the input functions indicate an end-of-file (or error) condition by returning EOF, which is a negative integer constant.
Output Functions
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
 int putchar(int c);
putchar writes one character to the stdout stream:
 putchar(ch);   /* writes ch to stdout */
fputc and putc are more general versions of putchar that write a character to an arbitrary stream:
fputc(ch, fp); /* writes ch to fp */
putc(ch, fp); /* writes ch to fp */
Although putc and fputc do the same thing, putc is usually implemented as a macro (as well as a function), while fputc is implemented only as a function, putchar itself is usually a macro defined in the following way:
#define putchar(c) putc((c), stdout)
It may seem odd that the library provides both putc and fputc. But, as we saw in  macros have several potential problems. The C standard allows the putc macro to evaluate the stream argument more than once, which fputc isn't permitted to do. Although programmers usually prefer putc, which gives a faster program, fputc is available as an alternative.
If a write error occurs, all three functions set the error indicator for the stream and return EOF; otherwise, they return the character that was written.


Input Functions
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);
getchar reads a character from the stdin stream:
ch = getchar(); /* reads a character from stdin */
fgetc and getc read a character from an arbitrary stream:
ch = fgetc(fp); /* reads a character from fp */
ch = getc(fp); /* reads a character from fp */

All three functions treat the character as an unsigned char value (which is then converted to int type before it's returned). As a result, they never return a nega­tive value other than EOF.
The relationship between getc and fgetc is similar to that between putc and fputc. getc is usually implemented as a macro (as well as a function), while fgetc is implemented only as a function, getchar is normally a macro as well:
#define getchar() getc(stdin)
For reading characters from a file, programmers usually prefer getc over fgetc. Since getc is normally available in macro form, it tends to be faster, fgetc can be used as a backup if getc isn't appropriate. (The standard allows the getc macro to evaluate its argument more than once, which may be a problem.)
The fgetc, getc. and getchar functions behave the same if a problem occurs. At end-of-file. they set the stream's end-of-file indicator and return EOF. If a read error occurs, they set the stream's error indicator and return EOF. To differ­entiate between the two situations, we can call either feof or ferror.
One of the most common uses of fgetc, getc, and getchar is to read characters from a file, one by one, until end-of-file occurs. It's customary to use the following while loop for that purpose:
while ((ch = getc(fp)) != EOF) {
…..
 }
After reading a character from the file associated with f p and storing it in the vari­able ch (which must be of type int), the while test compares ch with EOF. If ch isn't equal to EOF, we're not at the end of the file yet, so the body of the loop is executed. If ch is equal to EOF, the loop terminates.

Always store the return value of fgetc. getc, or getchar in an int variable, not a char variable. Testing a char variable against EOF may give the wrong, result.
There's one other character input function, ungetc, which "pushes back" a character read from a stream and clears the stream's end-of-file indicator. This capability can be handy if we need a "loofcahead" character during input. For instance, to read a series of digits, stopping at the first nondigit. we could write
while (isdigit(ch = getc(fp))) {
}
ungetc(ch, fp); /* pushes back last character read */
The number of characters that can be pushed back by consecutive calls of ungetc with no intervening read operations depends on the implementation and the type of stream involved; only the first call is guaranteed to succeed. Calling a file-positioning function (fseek, fsetpos, or rewind) causes the pushed- back characters to be lost.

ungetc returns the character it was asked to push back. However, it returns EOF if an attempt is made to push back EOF or to push back more characters than the implementation allows.





Line I/O
We'll now turn to library functions that read and write lines. These functions are used mostly with text streams, although it's legal to use them with binary streams as well.
Output Functions
int fputs(const char * restrict s, FILE * restrict stream);
int puts(const char *s);
The puts function it writes a string of characters to stdout:
puts("Hi, there!"); /* writes to stdout */
After it writes the characters in the string, puts always adds a new-line character.
The gets function, which we first encountered in Section 13.3, reads a line of input from stdin:
gets(str); /* reads a line from stdin */
gets reads characters one by one, storing them in the array pointed to by str, until it reads a new-line character (which it discards).

fgets is a more general version of gets that can read from any stream, f gets is also safer than gets, since it limits the number of characters that it will store. Here's how we might use fgets, assuming that str is the name of a char­acter array:
fgets(str, sizeof(str), fp); /* reads a line from fp */
This call will cause fgets to read characters until it reaches the first new-line character or sizeof (str) - 1 characters have been read, whichever happens first. If it reads the new-line character, fgets stores it along with the other charac­ters. (Thus, gets never stores the new-line character, but fgets sometimes does.)

Both gets and fgets return a null pointer if a read error occurs or they reach the end of the input stream before storing any characters. (As usual, we can call feof or ferror to determine which situation occurred.) Otherwise, both return their first argument, which points to the array in which the input was stored. As you'd expect, both functions store a null character at the end of the string.

Now that you know about fgets, I'd suggest using it instead of gets in most situations. With gets, there's always the possibility of stepping outside the bounds of the receiving array, so it's safe to use only when the string being read is guaranteed to fit into the array. When there's no guarantee (and there usually isn't), it's much safer to use fgets. Note that fgets will read from the standard input stream if passed stdin as its third argument:
fgets(str, sizeof(str), stdin);


Block I/O
size_t fread(void * restrict ptr,size_t size, size_t nmemb, FILE * restrict stream);
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
The fread and fwrite functions allow a program to read and write large blocks of data in a single step, fread and fwrite are used primarily with binary streams, although with care it's possible to use them with text streams as well.

fwrite is designed to copy an array from memory to a stream. The first argument in a call of fwrite is the array's address, the second argument is the size of each array element (in bytes), and the third argument is the number of ele­ments to write. The fourth argument is a file pointer, indicating where the data should be written. To write the entire contents of the array a, for instance, we could use the following call of fwrite:
fwrite(a, sizeof (a [0]), sizeof(a) / sizeof(a [0]), fp) ;
There's no rule that we have to write the entire array: we could just as easily write any portion of it. fwrite returns the number of elements (nor bytes) actu­ally written. This number will be less than the third argument if a write error oc­curs.

fread will read the elements of an array from a stream, f read's arguments are similar to fwrite's: the array's address, the size of each element (in bytes), the number of elements to read, and a file pointer. To read the contents of a file into the array a, we might use the following call of fread:
n = fread(a, sizeof(a [0]), sizeof(a) / sizeof(a[0]), fp) ;
It's important to check f read's return value, which indicates the actual number of elements (not bytes) read. This number should equal the third argument unless the end of the input file was reached or a read error occurred. The feof and ferror functions can be used to determine the reason for any shortage.

fwrite is convenient for a program that needs to store data in a file before terminating. Later, the program (or another program, for that matter) can use fread to read the data back into memory. Despite appearances, the data doesn't need to be in array form; fread and fwrite work just as well with variables of all kinds. Structures, in particular, can be read by fread or written by fwrite. To write a structure variable s to a file, for instance, we could use the following call of fwrite:
fwrite(&s, sizeof(s), 1, fp) ;
Be careful when using fwrite to write out structures that contain pointer values; these values aren't guaranteed to be valid when read back in.

No comments: