Peter S. Shenkin 写道:
> 我知道我有点迟钝,但是否有人能向我解释一下
> 这个问题是什么?我读了这个问题
> 和 Dann Corbitt 发布的 FAQ 部分,但我仍然不
> 明白。
>
> 你为什么要丢弃用户的输入,并且
> 你怎么会知道该丢弃哪一部分呢?
简短的回答是:“你不能,也不知道。”
这里发生的是在一个已有的(在某种意义上)“糟糕”的程序之上打了一个补丁。与其修复实际的问题,某些声称教授 C 的书籍建议修复症状。
这有点像去看医生,医生没有治疗初期肺炎,而是给你开了一种止咳药。
问题的根源在于使用scanf()。这个scanf()函数是一个庞大而复杂的怪物,它经常做一些接近但并非完全是你想要的事情。
整个scanf()系列通过解释“指令”来工作。这些指令并不适合交互式(“与人交谈”)输入。特别是,像“ ”或“\n”这样的指令的意思是:“跳过所有你能找到的输入空白符,包括换行符。”大多数转换指令(包括%d和%f)也隐含了跳过空白符。这意味着如果你打印一个提示符
printf("please enter a number: "); fflush(stdout);
然后使用 `scanf("%d", &var)' 获取输入,而用户输入了一个空行,程序只会停在那儿,而不会重新提示。
下一个问题是scanf()系列倾向于留下未被消耗的输入。任何“不符合要求”的输入都会被保留。如果用户应该输入一个数字,但输入了“three”而不是“3”,那么“t”就不符合“%d”的要求。整个行(“three\n”)会被留在输入流中。如果用户确实输入了一个数字,比如“3”,则只有换行符会留在流中。如果用户输入了一个数字后面跟着一个空格(或其他空白符),那么空格和换行符都会被留在流中。
这种(留下“额外”输入作为不谨慎者的陷阱的)特性导致人们编写“丢弃用户输入”的代码。不幸的是,他们经常使用特定于实现的构造,如fflush(stdin),或者像裸露的getchar()和scanf格式,如“%*[^\n]%*c”。要理解最后一个格式为何有问题,请阅读接下来的段落。
另一个与scanf()函数相关的严重问题是它们按顺序解释指令,并在遇到“匹配失败”时立即停止。这似乎经常让人们感到惊讶。特别是,考虑格式指令“%*[^\n]”。它由几个部分组成。“%”引入了转换。星号“*”抑制了转换结果的赋值,因此不需要额外的缓冲区。“[”指定转换进行字符类匹配,开头的“^”反转了类,而类本身只包含一个字符,“\n”。“]”终止了类,并且是该指令的结束。因此,该指令的意思是“匹配不是换行符的字符”。
这里的棘手之处在于任何%[指令必须至少匹配一个字符。如果它未能匹配至少一个字符,扫描就会终止。该调用会返回,而不查看任何进一步的指令。因此,如果下一个输入字符是换行符,“%*[”指令就会失败,而“%*c”则永远不会发生。格式序列“%*[^\n]%*c”仅在换行符之前至少有一个字符的情况下才清除该行的剩余部分。
正如其他人所指出的,这个问题可以通过使用两个单独的调用来修复。一个初始的scanf()带“%*[^\n]”将消耗直到换行符(但不包括换行符)的所有内容,或者失败。随后的“%*c”(或者普通的getchar())将消耗换行符(如果存在的话)。
最后一个“如果”也很重要:用户可能已经发送了 EOF。在这种情况下,getchar()或scanf("%*c")可能会——这个决定留给你的编译器编写者——要么立即返回 EOF,要么再次向用户请求更多输入。如果实现者选择后者,用户可能需要点击“结束这次输入”(^D, ^Z、鼠标按钮、前面板开关或任何其他方式)一次。这至少是很烦人的。
(顺便说一句,我凭印象认为%[和%c是唯一两个不会立即跳过空白符(包括换行符)的指令。)
那么“正确”的答案是什么?有多种方法可以做到。你可以编写极其复杂的代码,使用getchar,
ungetc,而scanf,并仔细检查所有返回值。你可以调用fgets()来读取一整行,然后——像“沙盒化”一样——使用sscanf(),并且不必太担心坏输入。你可以调用fgets()并使用strtol()和其他字符串解析函数。虽然所有更简单的方法都有一个共同的特点:它们首先读取一整行(包括结束的换行符),然后再尝试解析它。这给了用户时间来仔细考虑他们的答案,输入一些内容,删除它,输入其他内容,再次删除,再思考一下,然后通过按 ENTER 或 RETURN 来给出他们“最终的答案”给 Regis Philbin。 :-) 然后你一次性获得所有内容,并可以根据需要进行解析。
--
现实生活中:Chris Torek,Berkeley Software Design Inc
El Cerrito, CA, USA
http://claw.bsdi.com/torek/ (不一定总是在线) 我将垃圾邮件报告给 abuse@。
--
comp.lang.c.moderated - 审阅地址:clcm@plethora.net