faqcat1d60.html

来自「this is a mirrored site c-faq. thought 」· HTML 代码 · 共 2,607 行 · 第 1/5 页

HTML
2,607
字号
source code inlanguages like C, Pascal, and LISP is free-format,whiletraditionalBASIC and FORTRAN arenot.)</p><p>If you're insistent,<TT>scanf</TT> <em>can</em> be told to match a newline,using the ``scanset'' directive:<pre>	scanf("%d%*[\n]", &amp;n);</pre>Scansets,though powerful,won't solve all <TT>scanf</TT> problems,however.See also question <a href="faqcat1d60.html?sec=stdio#scanfprobs">12.20</a>.</p><p>References:K&amp;R2 Sec. B1.3 pp. 245-6<br>ISO Sec. 7.9.6.2<br>H&amp;S Sec. 15.8 pp. 357-64<hr><hr><hr><a name="scanfinterlace"><h1>comp.lang.c FAQ list<font color=blue>&middot;</font><a href="../../stdio/scanfinterlace.html"><!-- qtag -->Question 12.18a</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>I'm reading a number with <TT>scanf</TT>and<TT>%d</TT>,and then a string with<TT>gets()</TT>:<pre>	int n;	char str[80];	printf("enter a number: ");	scanf("%d", &amp;n);	printf("enter a string: ");	gets(str);	printf("you typed %d and \"%s\"\n", n, str);</pre>but the compiler seems to beskipping the call to <TT>gets()</TT>!</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>If, in response to the above program,you type the two lines<pre>	42	a string</pre><TT>scanf</TT> will read the 42,but <em>not</em> the newline following it.That newline will remain on the input stream,where it will immediately satisfy <TT>gets()</TT>(which willthereforeseem toreada blank line).The second line,``a&nbsp;string'',will not be read at all.</p><p>If you had happened to typeboth the number and the stringon the same line:<pre>	42 a string</pre>the codewould have worked more or less as you expected.</p><p>As a general rule,you shouldn't try tointerlacecalls to <TT>scanf</TT> with calls to <TT>gets()</TT>(or any other input routines);<TT>scanf</TT>'s peculiar treatment of newlinesalmostalwaysleads totrouble.Either use <TT>scanf</TT> to read everythingor nothing.</p><p>See also questions<a href="faqcat1d60.html?sec=stdio#scanfprobs">12.20</a>and<a href="faqcat1d60.html?sec=stdio#getsvsfgets">12.23</a>.</p><p>Additional links:<a href="../../stdio/gets_flush2.html">longer explanation</a></p><p>References:ISO Sec. 7.9.6.2<br>H&amp;S Sec. 15.8 pp. 357-64<hr><hr><hr><a name="scanfc"><h1>comp.lang.c FAQ list<font color=blue>&middot;</font><a href="../../stdio/scanfc.html"><!-- qtag -->Question 12.18b</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>I'm using <TT>scanf&nbsp;%c</TT> to read a Y/N response,but later input gets skipped.</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>You wanted <TT>scanf&nbsp;%c</TT> to read a single character,and it tried to,but when you tried to type that single character at it,before the rest of the input system would accept it,you had to hit the RETURN key, too.<TT>scanf</TT> read only the one character, but that extra newlinewas still sitting in an input buffer somewhere,and it's that extra newline(seemingly representing a phantom blank line)which was received by your later input call.See also questions<a href="faqcat1d60.html?sec=stdio#scanfinterlace">12.18a</a>and<a href="faqcat1d60.html?sec=stdio#scanfprobs">12.20</a>.<hr><hr><hr><a name="scanfjam"><h1>comp.lang.c FAQ list<font color=blue>&middot;</font><a href="../../stdio/scanfjam.html"><!-- qtag -->Question 12.19</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>I figured I could use <TT>scanf</TT> more safelyif I checked its return valueto make sure that the usertyped the numeric values I expect:<pre>	int n;	while(1) {		printf("enter a number: ");		if(scanf("%d", &amp;n) == 1)			break;		printf("try again: ");	}	printf("you typed %d\n", n);</pre>but sometimes it seems to go into an infinite loop.<a href="../../stdio/achtung.html" rel=subdocument>[footnote]</a>Why?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>When <TT>scanf</TT> is attempting to convert numbers,any non-numeric characters it encounters terminate theconversion<em>and are left on the input stream</em>.Therefore, unless some other steps are taken,unexpected non-numeric input ``jams'' <TT>scanf</TT>again and again:<TT>scanf</TT> never gets pastthe bad character(s)toencounterlater, valid data.If the user types a characterlike `<TT>x</TT>'in response tothe code above,the codewill loop printing``try again'' forever,butitwon't give the user a chance to try.</p><p>You may be wondering why <TT>scanf</TT>leavesunmatchable characters on the input stream.Supposeyou had a compact data file containing lines consisting of anumber and an alphabetic code string,without intervening whitespace, like<pre>	123CODE</pre>You might want to parse this data file with <TT>scanf</TT>,using the format string <TT>"%d%s"</TT>.But if the <TT>%d</TT> conversion did not leave the unmatchedcharacter on the input stream,<TT>%s</TT> would incorrectly read <TT>"ODE"</TT>instead of <TT>"CODE"</TT>.(The problem is a standard onein lexicalanalysis: when scanning an arbitrary-length numeric constant oralphanumeric identifier, you never know where it ends untilyou've read ``too far.''This isone reason that <TT>ungetc</TT> exists.)</p><p>See also question<a href="faqcat1d60.html?sec=stdio#scanfprobs">12.20</a>.</p><p>References:ISO Sec. 7.9.6.2<br>H&amp;S Sec. 15.8 pp. 357-64<hr><hr><hr><a name="achtung"><hr><hr><hr><a name="scanfprobs"><h1>comp.lang.c FAQ list<font color=blue>&middot;</font><a href="../../stdio/scanfprobs.html"><!-- qtag -->Question 12.20</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>Why does everyone say not to use <TT>scanf</TT>?What should I use instead?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font><TT>scanf</TT>hasa number of problems--see questions<a href="faqcat1d60.html?sec=stdio#scanfhang">12.17</a>,<a href="faqcat1d60.html?sec=stdio#scanfinterlace">12.18a</a>,and<a href="faqcat1d60.html?sec=stdio#scanfjam">12.19</a>.Also, its <TT>%s</TT>format has the same problemthat <TT>gets()</TT> has(see question <a href="faqcat1d60.html?sec=stdio#getsvsfgets">12.23</a>)--it'shard to guarantee thatthe receiving bufferwon't overflow.<a href="../../stdio/overflow.html" rel=subdocument>[footnote]</a></p><p>More generally,<TT>scanf</TT> is designed for relatively structured,formatted input(its name isin factderived from``scanformatted'').If you pay attention,it will tell you whether it succeeded or failed,but it can tell you only approximately where it failed,and not at all how or why.You have very little opportunity to do any error recovery.</p><p>Yet interactive user inputis the least structuredinput there is.A well-designed user interfacewill allow for the possibility of the user typing just about anything--notjust letters or punctuation when digits were expected,but also more or fewer characters than were expected,or no characters at all(i.e. just the RETURN key),or premature EOF,or anything.It's nearlyimpossibleto deal gracefullywith all of these potential problemswhen using <TT>scanf</TT>;it'sfareasierto read entire lines(with <TT>fgets</TT> or the like),then interpret them,either using <TT>sscanf</TT>or some other techniques.(Functions like<TT>strtol</TT>,<TT>strtok</TT>,and<TT>atoi</TT>are often useful;see alsoquestions <a href="faqcat1d60.html?sec=stdio#datafmts">12.16</a> and <a href="faqcat1067.html?sec=lib#strtok">13.6</a>.)If you do useany <TT>scanf</TT> variant,be sureto check the return valueto make sure that the expected number of items werefound.Also,if you use <TT>%s</TT>,be sure to guardagainst buffer overflow.</p><p>Note,by the way,thatcriticisms of<TT>scanf</TT>are not necessarily indictments of<TT>fscanf</TT>and <TT>sscanf</TT>.<TT>scanf</TT> reads from <TT>stdin</TT>,which is usually an interactive keyboardand is therefore the leastconstrained,leading to the most problems.When a data file has a known format,on the other hand,it may be appropriate to read it with <TT>fscanf</TT>.It's perfectly appropriate to parse strings with <TT>sscanf</TT>(as long as the return value is checked),because it's so easy to regain control,restart the scan,discard the input if it didn't match,etc.</p><p>Additional links:<UL><li><a href="../../stdio/gets_flush1.html">longer explanation</a> by Chris Torek<li><a href="../../stdio/gets_flush2.html">longer explanation</a> by yours truly</UL></p><p>References:K&amp;R2 Sec. 7.4 p. 159<hr><hr><hr><a name="sprintfsize"><h1>comp.lang.c FAQ list<font color=blue>&middot;</font><a href="../../stdio/sprintfsize.html"><!-- qtag -->Question 12.21</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>How can I tell how much destination buffer space I'll needfor an arbitrary <TT>sprintf</TT> call?How can I avoidoverflowingthe destination buffer with <TT>sprintf</TT>?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>When the format string being used with <TT>sprintf</TT> isknown andrelatively simple,you cansometimespredictabuffersize in an ad-hoc way.Ifthe formatconsists of one or two <TT>%s</TT>'s,you can count the fixedcharacters in the formatstring yourself(or let <TT>sizeof</TT> count them for you)and add in the result of calling <TT>strlen</TT>on the string(s) to be inserted.For example,tocompute the buffer size that the call<pre>	sprintf(buf, "You typed \"%s\"", answer);</pre>would need,you couldwrite:<pre>	int bufsize = 13 + strlen(answer);or	int bufsize = sizeof("You typed \"%s\"") + strlen(answer);</pre>followed by<pre>	char *buf = malloc(bufsize);	if(buf != NULL)		sprintf(buf, "You typed \"%s\"", answer);</pre>You canconservativelyestimate the size that <TT>%d</TT> will expand to withcode like:<pre>#include &lt;limits.h&gt;char buf[(sizeof(int) * CHAR_BIT + 2) / 3 + 1 + 1];sprintf(buf, "%d", n);</pre>This codecomputesthe number of characters requiredfor a base-8 representation of a number;a base-10 expansion is guaranteed to take as much room or less.(The <TT>+2</TT> takes care of truncation if the size is not amultiple of 3,and the <TT>+1+1</TT> leaves room for a leading <TT>-</TT>and a trailing <TT>\0</TT>.)Ananalogous techniquecould of course be used for <TT>long&nbsp;int</TT>,and the same buffer can obviously be used with<TT>%u</TT>, <TT>%o</TT>, and <TT>%x</TT>formatsas well.</p><p>When the format string is more complicated,orisnotevenknown until run time,predicting the buffer size becomes asdifficultas reimplementing <TT>sprintf</TT>,and correspondingly error-prone(and inadvisable).Alast-ditchtechniquewhich is sometimessuggestedis to use<TT>fprintf</TT> to print the same text to atemporary file,andthen tolook at <TT>fprintf</TT>'s return valueorthe size of the file(but see question <a href="faqcatea63.html?sec=osdep#filesize">19.12</a>).(Using a temporary file for this applicationis admittedlyclumsy and inelegant,<a href="../../stdio/diskerr.html" rel=subdocument>[footnote]</a>but it's the only portable solutionbesides writing an entire <TT>sprintf</TT> format interpreter.If your system provides one,you canusea null or ``bit bucket'' devicesuch as<TT>/dev/null</TT> or <TT>NUL</TT>instead of a temporary file.)</p><p>If there'sanychance that the buffer might not be big enough,you won't want to call <TT>sprintf</TT>without some guaranteethat the buffer will not overflowand overwrite some other part ofmemory.If the format string is known,you can limit <TT>%s</TT> expansion by using<TT>%.</TT>N<TT>s</TT>for some N,or <TT>%.*s</TT>(see also question <a href="faqcat1d60.html?sec=stdio#printfvwid">12.10</a>).</p><p>To avoidthe overflow problem,you can usea length-limited version of <TT>sprintf</TT>,namely <TT>snprintf</TT>.Itisused like this:<pre>	snprintf(buf, bufsize, "You typed \"%s\"", answer);</pre><TT>snprintf</TT>has been available in several stdio libraries(including GNU and 4.4bsd)for several years.Ithas finally beenstandardized in C99.</p><p>As an extra, added bonus,the C99 <TT>snprintf</TT> provides a wayto predict the size requiredfor an arbitrary <TT>sprintf</TT> call.C99's<TT>snprintf</TT> returnsthe number of characters it would have placed in the bufferif there were room,not just how many it did place.Furthermore,it may be called witha null pointeranda buffer sizeof 0

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?