我有以下程序:
int main(int argc, char *argv[])
{
char ch1, ch2;
printf("Input the first character:"); // Line 1
scanf("%c", &ch1);
printf("Input the second character:"); // Line 2
ch2 = getchar();
printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
printf("ch2=%c, ASCII code = %d\n", ch2, ch2);
system("PAUSE");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
正如上面代码的作者所解释的那样:程序将无法正常工作,因为在第1行,当用户按下Enter时,它将在输入缓冲区中留下2个字符:Enter key (ASCII code 13)和\n (ASCII code 10).因此,在第2行,它将读取\n并且不会等待用户输入字符.
好的,我明白了.但我的第一个问题是:为什么第二个getchar()(ch2 = getchar();)不读取Enter key (13)而不是\n字符?
接下来,作者提出了两种解决此类问题的方法:
使用 fflush()
写一个这样的函数:
void
clear (void)
{
while ( getchar() != '\n' );
}
Run Code Online (Sandbox Code Playgroud)这段代码实际上有效.但我无法解释自己是如何运作的?因为在while语句中,我们使用getchar() != '\n',这意味着读取任何单个字符除外'\n'?如果是这样,在输入缓冲区仍然是'\n'字符?
jam*_*lin 78
程序将无法正常工作,因为在第1行,当用户按Enter键时,它将在输入缓冲区中保留2个字符:输入密钥(ASCII代码13)和\n(ASCII代码10).因此,在第2行,它将读取\n并且不会等待用户输入字符.
您在第2行看到的行为是正确的,但这不是正确的解释.对于文本模式流,平台使用的行结尾无关紧要(无论是回车(0x0D)+换行(0x0A),裸CR还是裸LF).C运行时库将为您处理:您的程序只会看到'\n'换行符.
如果您键入了一个字符并按下了Enter,那么该输入字符将被第1行读取,然后'\n'将被第2行读取.请参阅我正在使用scanf %c读取Y/N响应,但后来的输入被跳过.来自comp.lang.c FAQ.
至于提出的解决方案,请参阅(再次从comp.lang.c常见问题解答):
这基本上表明唯一可移植的方法是:
int c;
while ((c = getchar()) != '\n' && c != EOF) { }
Run Code Online (Sandbox Code Playgroud)
您的getchar() != '\n'循环有效,因为一旦调用getchar(),返回的字符已经从输入流中删除.
另外,我觉得有必要阻止你scanf完全使用:为什么每个人都说不使用scanf?我应该用什么呢?
Ram*_*uri 39
你也可以这样做:
fseek(stdin,0,SEEK_END);
Run Code Online (Sandbox Code Playgroud)
Dmi*_*tri 11
线条:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
;
Run Code Online (Sandbox Code Playgroud)
不会只读取换行符('\n')之前的字符.它会读取流中的所有字符(并丢弃它们),直到并包括下一个换行符(或遇到EOF).为了使测试成立,它必须首先读取换行符; 所以当循环停止时,换行符是最后一个字符读取,但它已被读取.
至于为什么它读取换行而不是回车,这是因为系统已将回报转换为换行符.当按下enter时,表示该行的结束...但该流包含换行,因为这是系统的正常行尾标记.这可能取决于平台.
此外,fflush()在输入流上使用并不适用于所有平台; 例如,它通常不适用于Linux.
M.M*_*M.M 10
清除到您已尝试部分阅读的行尾的便携方式是:
int c;
while ( (c = getchar()) != '\n' && c != EOF ) { }
Run Code Online (Sandbox Code Playgroud)
这会读取并丢弃字符,直到它\n发出信号结束文件为止.EOF如果输入流在行结束之前关闭,它还会进行检查.c必须int(或更大)的类型才能保存该值EOF.
没有可移植的方法来确定当前行之后是否还有更多行(如果没有,getchar则会阻止输入).
但我无法解释自己是如何运作的?因为在while语句中,我们使用
getchar() != '\n',这意味着读取任何单个字符,除了'\n'?? 如果是这样,在输入缓冲区仍然是'\n'字符??? 我误解了什么吗?
您可能没有意识到的是,在从输入缓冲区中删除字符后 进行比较getchar().因此,当你到达时'\n',它被消耗,然后你就会脱离循环.
scanf是一个奇怪的函数,电影《战争游戏》中的一句经典台词与之相关:“唯一获胜的举动就是不玩”。
如果您发现自己需要“刷新输入”,那么您已经输了。获胜之举不是拼命寻找某种神奇的方法来刷新令人烦恼的输入:相反,您需要做的是以某种不同(更好)的方式进行输入,这不涉及在输入流上留下未读的输入,并将其放在那里并导致问题,因此您必须尝试冲洗它。
\n基本上有三种情况:
\n您正在使用 读取输入scanf,并且它将用户的换行符留在输入流上,并且稍后调用 或 会错误地读取该杂散getchar换行符fgets。(这就是您最初询问的情况。)
您正在使用 读取输入scanf,并且它将用户的换行符留在输入流上,并且稍后调用 时会错误地读取该杂散换行符scanf("%c")。
您正在使用 读取数字输入scanf,并且用户正在键入非数字文本,并且非数字文本留在输入流上,这意味着下一次调用也会scanf失败。
在所有这三种情况下,似乎正确的做法是“刷新”有问题的输入。你可以尝试,但往好里说是很麻烦,往坏了说是不可能的。最后,我认为尝试刷新输入是错误的方法,并且有更好的方法,具体取决于您担心的情况:
\n对于情况 1,更好的解决方案是,不要将scanf与其他输入函数的调用混合在一起。要么用 进行所有输入,要么用and/orscanf进行所有输入。要使用 进行所有输入,您可以将调用替换为\xe2\x80\x94,但请参阅第 2 点。理论上,您可以将调用替换为,尽管这会带来各种进一步的问题,我不建议这样做。即使您想要/需要一些\ 的解析,也要完成所有输入,您可以使用 读取行,然后使用 来解析它们。getcharfgetsscanfgetcharscanf("%c")fgetsscanf("%[^\\n]%*c")fgetsscanffgetssscanf
对于情况 2,更好的解决方案是永远不要使用scanf("%c"). 使用scanf(" %c")代替神奇的额外空间让一切变得不同。(对于这个额外空间的作用以及它为什么有帮助有很长的解释,但这超出了这个答案的范围。)
而对于情况3,恐怕根本就没有什么好的解决办法。 scanf有很多问题,其中之一就是它的错误处理很糟糕。如果您想编写一个读取数字输入的简单程序,并且您可以假设用户在提示时始终会键入正确的数字输入,那么scanf("%d")可以是足够的 \xe2\x80\x94\xc2\xa0 勉强足够的 \xe2\ x80\x94 输入法。但也许你的目标是做得更好。也许您想提示用户输入一些数字,并检查用户是否确实输入了数字,如果没有,则打印一条错误消息并要求用户重试。在这种情况下,我相信无论出于何种意图和目的,你都无法基于 来实现这一目标scanf。你可以尝试,但这就像给蠕动的婴儿穿上连体衣:将双腿和一只手臂放进去后,当你试图将另一只手臂放进去时,一条腿会蠕动出来。尝试使用 来读取和验证可能不正确的数字输入的工作量太大了scanf。以其他方式做到这一点要容易得多。
您会注意到贯穿我列出的所有三个案例的主题:它们都以“您正在使用scanf...读取输入”开头。这里有一个不可避免的结论。请参阅另一个问题:What can I use for input conversionrather not scanf?
现在,我意识到我仍然没有回答您实际提出的问题。当人们问“我该怎么做 X?”时,如果所有的答案都是“你不应该做 X”,那真的会令人沮丧。如果您真的非常想刷新输入,那么除了人们在这里给您的其他答案之外,另外两个充满相关答案的好问题是:
\n你可以试试
scanf("%c%*c", &ch1);
Run Code Online (Sandbox Code Playgroud)
其中%*c接受并忽略换行符
再一个方法,而不是fflush(stdin),它调用你可以写的未定义的行为
while((getchar())!='\n');
Run Code Online (Sandbox Code Playgroud)
不要忘记while循环后的分号
| 归档时间: |
|
| 查看次数: |
318466 次 |
| 最近记录: |