提问人:ipkiss 提问时间:3/9/2011 最后编辑:chqrlieipkiss 更新时间:9/8/2022 访问量:143859
scanf() 将换行符保留在缓冲区中
scanf() leaves the newline character in the buffer
问:
我有以下程序:
int main(int argc, char *argv[])
{
int a, b;
char c1, c2;
printf("Enter something: ");
scanf("%d", &a); // line 1
printf("Enter other something: ");
scanf("%d", &b); // line 2
printf("Enter a char: ");
scanf("%c", &c1); // line 3
printf("Enter another char: ");
scanf("%c", &c2); // line 4
printf("Done"); // line 5
system("PAUSE");
return 0;
}
正如我在 C 书中读到的那样,作者说在缓冲区中留下了一个换行符,因此,程序不会在第 4 行停止供用户输入数据,而是将换行符存储在第 5 行并移动到第 5 行。scanf()
c2
是吗?
但是,这是否只发生在数据类型上?因为我没有看到第 1、2、3 行的数据类型存在此问题。这是对的吗?char
int
答:
scanf()
函数在尝试解析字符以外的转换之前会自动跳过前导空格。字符格式(主要是;也是扫描集 - 和)是例外;它们不会跳过空格。%c
%[…]
%n
与前导空格一起使用可跳过可选的空格。不要在格式字符串中使用尾随空格。" %c"
scanf()
请注意,这仍然不会消耗输入流中留下的任何尾随空格,甚至不会消耗到行的末尾,因此,如果在同一输入流上也使用 getchar()
或 fgets(),
请注意这一点。我们只是在转换之前跳过空格,就像它对其他非字符转换所做的那样。%d
请注意,除转换之外的非空格“指令”(使用 POSIX scanf 术语),例如 中的文字文本也不会跳过空格。文本必须与要读取的下一个字符匹配。scanf("order = %d", &order);
order
因此,如果您想跳过上一行的换行符,但仍然需要固定字符串上的文字匹配,您可能需要那里,就像这个问题一样。" order = %d"
评论
%c
、 是 3 个指定的期望值,它们不占用前导空格。%n
%[]
scanf()
请求两次输入,而我希望它只询问一次以讨论格式字符串中的尾随空白
。它们是个坏主意——如果你期望人与人之间的交互,那就太糟糕了,而对程序交互来说却是坏事。
scanf()
用。这将解决您的问题。scanf(" %c", &c2);
在调用第二个之前使用。getchar()
scanf()
scanf("%c", &c1);
getchar(); // <== remove newline
scanf("%c", &c2);
评论
int c; while ((c = getchar()) != EOF && c != '\n') ;
int c; while ((c = getchar()) != EOF && c != '\n') ;
另一个选项(我从这里得到的)是使用 assignment-supression 选项读取和放弃换行符。为此,我们只需设置一种格式来读取 和 之间带有星号的字符:%
c
scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3
scanf
然后将读取下一个字符(即换行符),但不会将其分配给任何指针。
然而,最后,我会支持FAQ的最后一个选项:
或者,根据您的要求,您也可以忘记 scanf()/getchar(),使用 fgets() 从用户那里获取一行文本并自行解析。
评论
a
supercalifragilisticexpialidocious
a
scanf()
%
为了回应我在另一个关于 C++ 的答案中发布的内容:我建议扔掉它,永远不要使用它,而是使用 和.scanf()
fgets()
sscanf()
这样做的原因是,至少在类Unix系统中,默认情况下,运行CLI程序的终端在程序看到用户输入之前会对其进行一些处理。它缓冲输入,直到输入换行符,并允许进行一些基本的行编辑,例如使退格键起作用。
所以,你永远不可能一次得到一个字符,或者几个单一的字符,只有一整行。但这不是例如 进程,相反,它只处理数字,并停在那里,将其余部分缓冲在 C 库中,以供将来的函数使用。如果您的程序具有例如scanf("%d")
stdio
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
然后你输入行,它同时完成两个 s,但只有在给出换行符之后。当用户点击空格时,第一个不会返回,即使这是数字结束的地方(因为此时该行仍在终端的行缓冲区中);第二个不会等待您输入另一行(因为输入缓冲区已经包含足够的内容来填充转换)。123 abcd
scanf()
scanf()
scanf()
%s
这不是用户通常所期望的!
相反,他们希望按回车键完成输入,如果你按回车键,你要么得到一个默认值,要么得到一个错误,并可能建议真的给出答案。
你不能真的用 .如果用户只是按回车键,则不会发生任何事情。因为还在等号码。终端会继续发送线路,但您的程序看不到它,因为会吃掉它。你没有机会对用户的错误做出反应。scanf("%d")
scanf()
scanf()
那也不是很有用。
因此,我建议一次使用 Or 读取一整行输入。这与终端提供的内容完全匹配,并且始终在用户输入线路后为您提供程序控制权。你对输入行的处理取决于你,如果你想要一个数字,你可以使用 、 ,甚至解析这个数字。 与 没有相同的不匹配,因为它从中读取的缓冲区大小有限,当它结束时,它就结束了——函数不能等待更多。fgets()
getline()
atoi()
strtol()
sscanf(buf, "%d", &a)
sscanf()
scanf()
(fscanf()
如果文件格式支持它如何像任何空格一样浏览换行符,那么常规文件也可以。对于面向行的数据,我仍然会使用 fgets()
和 sscanf()。
所以,而不是我上面的内容,使用这样的东西:
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
或者,实际上,检查 too 的返回值,以便您可以检测空行和其他无效数据:sscanf()
#include <stdio.h>
int main(void)
{
const int bufsize = 100;
char buf[bufsize];
int a;
int ret;
char word[bufsize];
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%d", &a);
if (ret != 1) {
fprintf(stderr, "Ok, you don't have to.\n");
return 1;
}
printf("Enter a word: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%s", word);
if (ret != 1) {
fprintf(stderr, "You make me sad.\n");
return 1;
}
printf("You entered %d and %s\n", a, word);
}
当然,如果你想让程序坚持下去,你可以创建一个简单的函数来循环 和 直到用户屈尊去做他们被告知的事情;或者立即退出并出现错误。取决于你认为如果用户不想打球,你的程序应该做什么。fgets()
sscanf()
你可以做类似的事情,例如,通过循环读取字符,直到返回换行符,从而清除缓冲区中留下的任何垃圾,但这对用户只是在空行上按回车键的情况没有任何作用。无论如何,会一直读到换行符,所以你不必自己做。getchar()
scanf("%d")
fgets()
评论
fgets()
/getline()
而then or ,等等,也比直接使用还有另一个巨大的优势。当程序遇到意外输入时,输入流不会处于未知状态,在这种状态下,在不丢失数据的情况下进行恢复是不可能的。sscanf()
strtol()
scanf()
/*Take char input using scanf after int input using scanf just use fflush(stdin) function after int input */
#include<stdio.h>
#include<conio.h>
void main()
{
int x;
char y;
clrscr();
printf(" enter an int ");
scanf("%d",&x);
fflush(stdin);
printf("\n Now enter a char");
scanf("%c",&y);
printf("\n X=%d and Y=%c",x,y);
getch();
}
评论
fflush(stdin)
的注意事项。
conio.h
不是 ISO C 的一部分,而是特定于平台的标头。这些功能也是特定于平台的。请注意,该问题未标记任何特定平台,因此该问题不适用于任何特定平台。因此,如果您决定提供特定于平台的答案,您至少应该清楚地将其标记为此类答案,并指定您的答案适用于哪个平台。clrscr
getch
两种解决方法。一种是巧妙地捕捉多余的空格。
getchar(); // (1) add `getchar()`
scanf("%c", &c1);
scanf(" %c", &c1); // (2) add whitespace before %c
另一种是用 to 代替。注意:是不安全的,看看为什么得到是危险的fgets()
scanf()
gets()
相比之下,我更愿意推荐第二种方式。因为它更具可读性和可维护性。例如,在第一种方式中,您必须在添加/移动一些 .scanf()
评论
fflush(stdin)
来讨论该方法的优缺点和替代方案(该方法或多或少在 Windows 上有效,并且在大多数其他地方不起作用)。fflush(stdin)
scanf()
fflush(stdin)