C67. C Formatted I/O


Formatted I/O
In this section, we'll examine library functions that use format strings to control reading and writing. These functions, which include our old friends printf and scanf, have the ability to convert data from character form to numeric form dur­ing input and from numeric form to character form during output. None of the other I/O functions can do such conversions.
The ...printf Functions
int fprintf(FILE * restrict stream,const char * restrict format,…);
int printf(const char * restrict format, …);
The fprintf and printf functions write a variable number of data items to an output stream, using a format string to control the appearance of the output. The prototypes for both functions end with the ... symbol (an ellipsis), which indicates a variable number of additional arguments. Both functions return the number of characters written; a negative return value indicates that an error occurred.
The only difference between printf and fprintf is that printf always writes to stdout (the standard output stream), whereas fprintf writes to the stream indicated by its first argument:
printf("Total: %d\n", total); /* writes to stdout */

fprintf(fp, "Total: %d\n", total); /* writes to fp */
A call of printf is equivalent to a call of fprintf with stdout as the first argument.
Don't think of fprintf as merely a function that writes data to disk files, though. Like many functions in , fprintf works fine with any out­put stream. In fact, one of the most common uses of fprintf writing error messages to stderr. the standard error stream has nothing to do with disk files. Here's what such a call might look like:
fprintf(stderr, "Error: data file can't be opened.\n");
Writing the message to stderr guarantees that it will appear on the screen even if the user redirects stdout.
There are two other functions in that can write formatted output to a stream. These functions, named vfprintf and vprintf. are fairly obscure. Both rely on the va list type, which is declared in , so they're discussed along with that header.















...printf Conversion Specifications
Both printf and fprintf require a format string containing ordinary charac­ters and/or conversion specifications. Ordinary characters are printed as is; conver­sion specifications describe how the remaining arguments are to be converted to character form for display. We'll now review what we know about conversion specifications and fill in the remaining gaps.
A ...printf conversion specification consists of the % character, followed by as many as five distinct items:

Flag
Meaning
-           Left-justify within field. (The default is right justification.)
+
Numbers produced by signed conversions always begin with + or -. (Normally, only negative numbers are preceded by a sign.)
space
Nonnegative numbers produced by signed conversions are preceded by a space. (The + flag overrides the space flag.)
#
Octal numbers begin with 0. nonzero hexadecimal numbers with Ox or OX. Floating-point numbers always have a decimal point Trailing zeros aren't removed from numbers printed with the g or G conversions.
0
(zero)
Numbers are padded with leading zeros up to the Held width. The 0 flag is ignored if the conversion is d. i, o. u, x, or X and a precision is specified. (The - flag overrides the 0 flag.)


The ...scanf Functions
int fscanf(FILE * restrict stream,const char * restrict format, ...);
 int scanf(const char * restrict format, ...);
fscanf and scanf read data items from an input stream, using a format string to indicate the layout of the input. After the format string, any number of pointers each pointing to an object follow as additional arguments. Input items are con­verted (according to conversion specifications in the format string) and stored in these objects.
scanf always reads from stdin (the standard input stream), whereas fscanf reads from the stream indicated by its first argument:
scanf ("%d%d", &i, Uj ) ;  /* reads from stdin */
fscanf(fp, "%d%d", &i, &j); /* reads from fp */
A call of scanf is equivalent to a call of fscanf with stdin as the first argu­ment.

The ...scanf functions return prematurely if an input failure occurs (no more input characters could be read) or if a matching failure occurs (the input charac­ters didn't match the format string). (In C99. an input failure can also occur because of an encoding error, which means that an attempt was made to read a multibyte character, but the input characters didn't correspond to any valid multi- byte character.) Both functions return the number of data items that were read and assigned to objects: they return EOF if an input failure occurs before any data items can be read.




Loops that test scanf's return value are common in C programs. The follow­ing loop, for example, reads a series of integers one by one. stopping at the first sign of trouble:

while (scanf("%d", &i) == 1) {….
 }
...scanf Format Strings
Calls of the ...scanf functions resemble those of the ...printf functions. That similarity can be misleading, however; the ...scanf functions work quite differ­ently from the ...printf functions. It pays to think of scanf and fscanf as "pattern-matching" functions. The format string represents a pattern that a ...scanf function attempts to match as it reads input. If the input doesn't match the format string, the function returns as soon as it detects the mismatch; the input character that didn't match is "pushed back" to be read in the future. A ...scanf format string may contain three things:
Conversion specifications. Conversion specifications in a ...scanf format string resemble those in a ...printf format string. Most conversion specifica­tions skip white-space characters at the beginning of an input item (the excep­tions are % [. %c. and %n). Conversion specifications never skip trailing white- space characters, however. If the input contains •123°”, the %d conversion specification consumes • 1, 2, and 3, but leaves o unread. (I'm using • to represent the space character and ° to represent the new-line character.)

White-space characters. One or more consecutive white-space characters in a ...scanf format string match zero or more white-space characters in the input stream.

Non-white-space characters. A non-white-space character other than % matches the same character in the input stream.

For example, the format string "ISBN %d-%d-%ld-%d" specifies that the input will consist of: the letters ISBN
possibly some white-space characters an integer the - character
an integer (possibly preceded by white-space characters) the - character
a long integer (possibly preceded by white-space characters) the - character
an integer (possibly preceded by white-space characters) ...scanf Conversion Specifications
Conversion specifications for ...scanf functions are actually a little simpler than those for ...printf functions. A ...scanf conversion specification consists of the character % followed by the items listed below (in the order shown).

* (optional). The presence of * signifies assignment suppression: an input item is read but not assigned to an object. Items matched using * aren't included in the count that ...scanf returns.

Maximum field width (optional). The maximum field width limits the number of characters in an input item; conversion of the item ends if this number is reached. White-space characters skipped at the beginning of a conversion don't count.

Length modifier (optional). The presence of a length modifier indicates that the object in which the input item will be stored has a type that's longer or shorter than is normal for a particular conversion specification.

Detecting End-of-File and Error Conditions
void clearerr(FILE *stream); int feof(FILE *stream);
 int f error (FILE *stream);
If we ask a ...scanf function to read and store n data items, we expect its return value to be n. If the return value is less than n, something went wrong. There are three possibilities:
End-of-file. The function encountered end-of-file before matching the format string completely.
Read error. The function was unable to read characters from the stream.
Matching failure. A data item was in the wrong format. For example, the function might have encountered a letter while searching for the first digit of an integer.
But how can we tell which kind of failure occurred? In many cases, it doesn't mat­ter; something went wrong, and we've got to abandon the program. There may be times, however, when we'll need to pinpoint the reason for the failure.

Every stream has two indicators associated with it: an error indicator and an end-of-file indicator. These indicators are cleared when the stream is opened. Not surprisingly, encountering end-of-file sets the end-of-file indicator, and a read error sets the error indicator. (The error indicator is also set when a write error occurs on an output stream.) A matching failure doesn't change either indicator.

Once the error or end-of-file indicator is set, it remains in that state until it's explicitly cleared, perhaps by a call of the clearerr function, clearerr clears both the end-of-file and error indicators:
clearerr(fp); /+ clears eof and error indicators for fp */

clearerr isn't needed often, since some of the other library functions clear one or both indicators as a side effect.
We can call the f eof and ferror functions to test a stream's indicators to determine why a prior operation on the stream failed. The call feof (fp) returns a nonzero value if the end-of-file indicator is set for the stream associated with f p. The call ferror (fp) returns a nonzero value if the error indicator is set. Both functions return zero otherwise.
When scanf returns a smaller-than-expected value, we can use feof and ferror to determine the reason. If feof returns a nonzero value, we've reached the end of the input file. If ferror returns a nonzero value, a read error occurred during input. If neither returns a nonzero value, a matching failure must have oc­curred. Regardless of what the problem was, the return value of scanf tells us how many data items were read before the problem occurred.

No comments: