📄 chapter7.html
字号:
<h2><a name="s7.4">7.4 Formatted Input - Scanf</a></h2>The function <tt>scanf</tt> is the input analog of <tt>printf</tt>, providing manyof the same conversion facilities in the opposite direction.<pre> int scanf(char *format, ...)</pre><tt>scanf</tt> reads characters from the standard input, interprets themaccording to the specification in <tt>format</tt>, and stores the resultsthrough the remaining arguments. The format argument is described below; theother arguments, <em>each of which must be a pointer</em>, indicate where thecorresponding converted input should be stored. As with <tt>printf</tt>, thissection is a summary of the most useful features, not an exhaustive list.<p><tt>scanf</tt> stops when it exhausts its format string, or when some inputfails to match the control specification. It returns as its value the numberof successfully matched and assigned input items. This can be used to decidehow many items were found. On the end of file, <tt>EOF</tt> is returned; notethat this is different from 0, which means that the next input character doesnot match the first specification in the format string. The next call to<tt>scanf</tt> resumes searching immediately after the last character alreadyconverted.<p>There is also a function <tt>sscanf</tt> that reads from a string instead ofthe standard input:<pre> int sscanf(char *string, char *format, arg1, arg2, ...)</pre>It scans the <tt>string</tt> according to the format in <tt>format</tt> andstores the resulting values through <tt>arg1</tt>, <tt>arg2</tt>, etc. Thesearguments must be pointers.<p>The format string usually contains conversion specifications, which are usedto control conversion of input. The format string may contain:<ul><li>Blanks or tabs, which are not ignored.<li>Ordinary characters (not %), which are expected to match the next non-white space character of the input stream.<li>Conversion specifications, consisting of the character <tt>%</tt>, an optional assignment suppression character <tt>*</tt>, an optional number specifying a maximum field width, an optional <tt>h</tt>, <tt>l</tt> or <tt>L</tt> indicating the width of the target, and a conversion character.</ul>A conversion specification directs the conversion of the next input field.Normally the result is places in the variable pointed to by the correspondingargument. If assignment suppression is indicated by the * character, however,the input field is skipped; no assignment is made. An input field is definedas a string of non-white space characters; it extends either to the nextwhite space character or until the field width, is specified, is exhausted.This implies that <tt>scanf</tt> will read across boundaries to find itsinput, since newlines are white space. (White space characters are blank,tab, newline, carriage return, vertical tab, and formfeed.)<p>The conversion character indicates the interpretation of the input field. Thecorresponding argument must be a pointer, as required by the call-by-valuesemantics of C. Conversion characters are shown in Table 7.2.<p align="center"><em><strong>Table 7.2:</strong> Basic Scanf Conversions</em><table align="center" border=1><th align="center">Character<th align="center">Input Data; Argument type<tr><td><tt>d</tt></td><td>decimal integer; <tt>int *</tt></td><tr><td><tt>i</tt></td><td>integer; <tt>int *</tt>. The integer may be in octal (leading <tt>0</tt>) or hexadecimal (leading <tt>0x</tt> or <tt>0X</tt>).</td><tr><td><tt>o</tt></td><td>octal integer (with or without leading zero); <tt>int *</tt></td><tr><td><tt>u</tt></td><td>unsigned decimal integer; <tt>unsigned int *</tt></td><tr><td><tt>x</tt></td><td>hexadecimal integer (with or without leading <tt>0x</tt> or <tt>0X</tt>); <tt>int *</tt></td><tr><td><tt>c</tt></td><td>characters; <tt>char *</tt>. The next input characters (default 1) are placed at the indicated spot. The normal skip-over white space is suppressed; to read the next non-white space character, use <tt>%1s</tt></td><tr><td><tt>s</tt></td><td>character string (not quoted); <tt>char *</tt>, pointing to an array of characters long enough for the string and a terminating <tt>'\0'</tt> that will be added.</td><tr><td><tt>e,f,g</tt></td><td>floating-point number with optional sign, optional decimal point and optional exponent; <tt>float *</tt></td><tr><td><tt>%</tt></td><td>literal %; no assignment is made.</td></table><p>The conversion characters <tt>d</tt>, <tt>i</tt>, <tt>o</tt>, <tt>u</tt>, and<tt>x</tt> may be preceded by <tt>h</tt> to indicate that a pointer to<tt>short</tt> rather than <tt>int</tt> appears in the argument list, or by<tt>l</tt> (letter ell) to indicate that a pointer to <tt>long</tt> appearsin the argument list.<p>As a first example, the rudimentary calculator of <a href="chapter4.html">Chapter 4</a>can be written with <tt>scanf</tt> to do the input conversion:<pre> #include <stdio.h> main() /* rudimentary calculator */ { double sum, v; sum = 0; while (scanf("%lf", &v) == 1) printf("\t%.2f\n", sum += v); return 0; }</pre>Suppose we want to read input lines that contain dates of the form<pre> 25 Dec 1988</pre>The <tt>scanf</tt> statement is<pre> int day, year; char monthname[20]; scanf("%d %s %d", &day, monthname, &year);</pre>No <tt>&</tt> is used with <tt>monthname</tt>, since an array name is a pointer.<p>Literal characters can appear in the <tt>scanf</tt> format string; they mustmatch the same characters in the input. So we could read dates of the form<tt>mm/dd/yy</tt> with the <tt>scanf</tt> statement:<pre> int day, month, year; scanf("%d/%d/%d", &month, &day, &year);</pre><tt>scanf</tt> ignores blanks and tabs in its format string. Furthermore, itskips over white space (blanks, tabs, newlines, etc.) as it looks for inputvalues. To read input whose format is not fixed, it is often best to read aline at a time, then pick it apart with <tt>scanf</tt>. For example, supposewe want to read lines that might contain a date in either of the forms above.Then we could write<pre> while (getline(line, sizeof(line)) > 0) { if (sscanf(line, "%d %s %d", &day, monthname, &year) == 3) printf("valid: %s\n", line); /* 25 Dec 1988 form */ else if (sscanf(line, "%d/%d/%d", &month, &day, &year) == 3) printf("valid: %s\n", line); /* mm/dd/yy form */ else printf("invalid: %s\n", line); /* invalid form */ }</pre>Calls to <tt>scanf</tt> can be mixed with calls to other input functions. Thenext call to any input function will begin by reading the first character notread by <tt>scanf</tt>.<p>A final warning: the arguments to <tt>scanf</tt> and <tt>sscanf</tt><em>must</em> be pointers. By far the most common error is writing<pre> scanf("%d", n);</pre>instead of<pre> scanf("%d", &n);</pre>This error is not generally detected at compile time.<p><strong>Exercise 7-4.</strong> Write a private version of <tt>scanf</tt>analogous to <tt>minprintf</tt> from the previous section.<p><strong>Exercise 5-5.</strong> Rewrite the postfix calculator of<a href="chapter4.html">Chapter 4</a> to use <tt>scanf</tt> and/or<tt>sscanf</tt> to do the input and number conversion.<h2><a name="s7.5">7.5 File Access</a></h2>The examples so far have all read the standard input and written thestandard output, which are automatically defined for a program by the localoperating system.<p>The next step is to write a program that accesses a file that is <em>not</em>already connected to the program. One program that illustrates the need forsuch operations is <tt>cat</tt>, which concatenates a set of named files intothe standard output. <tt>cat</tt> is used for printing files on the screen,and as a general-purpose input collector for programs that do not have thecapability of accessing files by name. For example, the command<pre> cat x.c y.c</pre>prints the contents of the files <tt>x.c</tt> and <tt>y.c</tt> (and nothingelse) on the standard output.<p>The question is how to arrange for the named files to be read - that is, howto connect the external names that a user thinks of to the statements thatread the data.<p>The rules are simple. Before it can be read or written, a file has to be<em>opened</em> by the library function <tt>fopen</tt>. <tt>fopen</tt> takesan external name like <tt>x.c</tt> or <tt>y.c</tt>, does some housekeepingand negotiation with the operating system (details of which needn't concernus), and returns a pointer to be used in subsequent reads or writes of thefile.<p>This pointer, called the <em>file pointer</em>, points to a structure thatcontains information about the file, such as the location of a buffer, thecurrent character position in the buffer, whether the file is being read orwritten, and whether errors or end of file have occurred. Users don't needto know the details, because the definitions obtained from<tt><stdio.h></tt> include a structure declaration called<tt>FILE</tt>. The only declaration needed for a file pointer is exemplifiedby<pre> FILE *fp; FILE *fopen(char *name, char *mode);</pre>This says that <tt>fp</tt> is a pointer to a <tt>FILE</tt>, and<tt>fopen</tt> returns a pointer to a <tt>FILE</tt>. Notice that<tt>FILE</tt> is a type name, like <tt>int</tt>, not a structure tag; it isdefined with a <tt>typedef</tt>. (Details of how <tt>fopen</tt> can beimplemented on the UNIX system are given in <ahref="chapter8.html#s8.5">Section 8.5</a>.)<p>The call to <tt>fopen</tt> in a program is<pre> fp = fopen(name, mode);</pre>The first argument of <tt>fopen</tt> is a character string containing thename of the file. The second argument is the <em>mode</em>, also a characterstring, which indicates how one intends to use the file. Allowable modesinclude read (<tt>"r"</tt>), write (<tt>"w"</tt>), and append (<tt>"a"</tt>).Some systems distinguish between text and binary files; for the latter, a<tt>"b"</tt> must be appended to the mode string.<p>If a file that does not exist is opened for writing or appending, it iscreated if possible. Opening an existing file for writing causes the oldcontents to be discarded, while opening for appending preserves them. Tryingto read a file that does not exist is an error, and there may be other causesof error as well, like trying to read a file when you don't have permission.If there is any error, <tt>fopen</tt> will return <tt>NULL</tt>. (The errorcan be identified more precisely; see the discussion of error-handlingfunctions at the end of <a href="appb.html#sb.1">Section 1 in Appendix B</a>.)<p>The next thing needed is a way to read or write the file once it is open.<tt>getc</tt> returns the next character from a file; it needs the filepointer to tell it which file.<pre> int getc(FILE *fp)</pre><tt>getc</tt> returns the next character from the stream referred to by<tt>fp</tt>; it returns <tt>EOF</tt> for end of file or error.<p><tt>putc</tt> is an output function:<pre> int putc(int c, FILE *fp)</pre><tt>putc</tt> writes the character <tt>c</tt> to the file <tt>fp</tt>and returns the character written, or EOF if an error occurs. Like<tt>getchar</tt> and <tt>putchar</tt>, <tt>getc</tt> and <tt>putc</tt>may be macros instead of functions.<p>When a C program is started, the operating system environment is responsiblefor opening three files and providing pointers for them. These files are thestandard input, the standard output, and the standard error; thecorresponding file pointers are called <tt>stdin</tt>, <tt>stdout</tt>, and<tt>stderr</tt>, and are declared in <tt><stdio.h></tt>. Normally<tt>stdin</tt> is connected to the keyboard and <tt>stdout</tt> and<tt>stderr</tt> are connected to the screen, but <tt>stdin</tt> and<tt>stdout</tt> may be redirected to files or pipes as described in<a href="chapter7.html#s7.1">Section 7.1</a>.<p><tt>getchar</tt> and <tt>putchar</tt> can be defined in terms of <tt>getc</tt>,<tt>putc</tt>, <tt>stdin</tt>, and <tt>stdout</tt> as follows:<pre> #define getchar() getc(stdin) #define putchar(c) putc((c), stdout)</pre>For formatted input or output of files, the functions <tt>fscanf</tt> and<tt>fprintf</tt> may be used. These are identical to <tt>scanf</tt> and<tt>printf</tt>, except that the first argument is a file pointer thatspecifies the file to be read or written; the format string is the secondargument.<pre> int fscanf(FILE *fp, char *format, ...) int fprintf(FILE *fp, char *format, ...)</pre>With these preliminaries out of the way, we are now in a position to writethe program <tt>cat</tt> to concatenate files. The design is one that hasbeen found convenient for many programs. If there are command-line arguments,they are interpreted as filenames, and processed in order. If there are noarguments, the standard input is processed.<pre> #include <stdio.h> /* cat: concatenate files, version 1 */ main(int argc, char *argv[]) { FILE *fp; void filecopy(FILE *, FILE *) if (argc == 1) /* no args; copy standard input */ filecopy(stdin, stdout); else while(--argc > 0) if ((fp = fopen(*++argv, "r")) == NULL) { printf("cat: can't open %s\n, *argv); return 1; } else { filecopy(fp, stdout); fclose(fp); } return 0; } /* filecopy: copy file ifp to file ofp */ void filecopy(FILE *ifp, FILE *ofp) { int c; while ((c = getc(ifp)) != EOF) putc(c, ofp); }</pre>The file pointers <tt>stdin</tt> and <tt>stdout</tt> are objects of type<tt>FILE *</tt>. They are constants, however, <em>not</em> variables, so itis not possible to assign to them.<p>The function<pre> int fclose(FILE *fp)</pre>is the inverse of <tt>fopen</tt>, it breaks the connection between the filepointer and the external name that was established by <tt>fopen</tt>, freeingthe file pointer for another file. Since most operating systems have somelimit on the number of files that a program may have open simultaneously,it's a good idea to free the file pointers when they are no longer needed, aswe did in <tt>cat</tt>. There is also another reason for <tt>fclose</tt> onan output file - it flushes the buffer in which <tt>putc</tt> is collectingoutput. <tt>fclose</tt> is called automatically for each open file when aprogram terminates normally. (You can close <tt>stdin</tt> and <tt>stdout</tt>if they are not needed. They can also be reassigned by the library function<tt>freopen</tt>.)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -