scanf()将新行char留在缓冲区中

ipk*_*iss 71 c scanf

我有以下程序:

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;
}
Run Code Online (Sandbox Code Playgroud)

正如我在C书中读到的那样,作者说scanf()在缓冲区中留下了一个新的行字符,因此,程序不会在第4行停止供用户输入数据,而是将新行字符存储在c2中并移至第5行.

是对的吗?

但是,这只发生在char数据类型中吗?因为我没有int在第1,2,3行中看到数据类型的这个问题.是不是?

Jer*_*ock 62

scanf()函数在尝试解析字符以外的转换之前会自动删除空格.字符格式(主要是%c;也是扫描集%[…]- 和%n)是例外; 他们不删除空格.

使用" %c"前导空白跳过可选空格.不要在scanf()格式字符串中使用尾随空白.

请注意,这仍然不会消耗输入流中留下的任何尾随空格,甚至不会消耗到行尾,因此请注意,如果也使用getchar()fgets()在同一输入流上.我们只是让scanf 转换之前跳过空格,就像它%d和其他非字符转换一样.


请注意,除了转换之外的非空白"指令"(使用POSIX scanf术语),如文字文本scanf("order = %d", &order);也不会跳过空格.文字order必须匹配要读取的下一个字符.

所以你可能想要" order = %d"在那里跳过前一行的换行但仍然需要在固定字符串上进行文字匹配,就像这个问题一样.

  • `%c`,`%n`,`%[]`是3个指定的期望,不消耗前导空格. (7认同)
  • 请参阅 [`scanf()` 格式字符串中的尾随空白](http://stackoverflow.com/questions/19499060/what-is-difference-between-scanfd-and-scanfd) 和 [`scanf()` 询问两次输入,而我希望它只问一次](http://stackoverflow.com/questions/15740024/scanf-asking-twice-for-input-while-i-expect-it-to-ask-only-one/15740124 #15740124) 讨论格式字符串中的尾随空白。它们是个坏主意——如果你期望人类交互和程序交互,那就太糟糕了。 (4认同)

Shw*_*eta 30

使用scanf(" %c", &c2);.这将解决您的问题.


ilk*_*chu 11

为了呼应我在另一个关于 C++ 的答案中发布的内容:我建议扔掉scanf(),永远不要使用它,而是使用fgets()and sscanf()

原因是,至少在默认情况下类 Unix 系统中,运行 CLI 程序的终端会在程序看到用户输入之前对用户输入进行一些处理。它会缓冲输入,直到输入换行符,并允许进行一些基本的行编辑,例如使退格键起作用。

因此,您永远无法一次获得一个字符,或者几个单个字符,而只是一整行。但这不是egscanf("%d")处理的内容,而是仅处理数字,然后停在那里,将其余部分缓冲在C 库中,以供将来的stdio函数使用。如果你的程序有例如

printf("Enter a number: ");
scanf("%d", &a);

printf("Enter a word: ");
scanf("%s", word);
Run Code Online (Sandbox Code Playgroud)

当你输入 line 时,它​​会同时123 abcd完成两个s ,但只有在给出换行符之后才会完成。 scanf()当用户点击空格时,第一个scanf()不会返回,即使这是数字结束的地方(因为此时该行仍在终端的行缓冲区中);第二个scanf()不会等待您输入另一行(因为输入缓冲区已经包含足够的内容来填充转换%s)。

这不是用户通常所期望的!

相反,他们期望按回车键完成输入,如果你按回车键,你要么得到一个默认值,要么得到一个错误,可能会出现一条建议,请真正给出答案。

你确实不能用 来做到这一点scanf("%d")。如果用户只是按回车键,则不会发生任何事情。因为scanf()还在等号。终端向前发送该行,但您的程序看不到它,因为scanf()它被吃掉了。您没有机会对用户的错误做出反应。

这也不是很有用。

因此,我建议一次使用fgets()或读取一整行输入。getline()这与终端给出的内容完全匹配,并且在用户输入一行后始终为您的程序提供控制权。您对输入行的处理取决于您,如果您想要一个数字,您可以使用atoi()strtol()、 甚至sscanf(buf, "%d", &a)解析该数字。sscanf()没有与 相同的不匹配scanf(),因为它读取的缓冲区大小有限,当它结束时,它就结束了——该函数不能等待更多。

fscanf()如果文件格式支持如何像任何空格一样跳过换行符,那么在常规文件上也可以。对于面向行的数据,我仍然使用fgets()and sscanf()。)


因此,不要使用上面的内容,而是使用这样的内容:

printf("Enter a number: ");

fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
Run Code Online (Sandbox Code Playgroud)

或者,实际上,检查too的返回值sscanf(),这样您就可以检测空行和其他无效数据:

printf("Enter a number: ");
scanf("%d", &a);

printf("Enter a word: ");
scanf("%s", word);
Run Code Online (Sandbox Code Playgroud)

当然,如果您希望程序坚持执行,您可以创建一个简单的函数来循环 和 ,fgets()直到sscanf()用户屈尊执行他们被告知的事情;或者立即退出并出现错误。取决于您认为如果用户不想玩球,您的程序应该做什么。


您可以做类似的事情,例如通过循环getchar()读取字符直到scanf("%d")返回后换行,从而清除缓冲区中留下的任何垃圾,但这对于用户只是在空行上按 Enter 键的情况没有任何作用。不管怎样,fgets()会读到换行符,所以你不必自己做。


bra*_*zzi 8

另一种选择(我从这里得到的)是通过使用assignment-supression option读取和丢弃换行符。为此,我们只需放置一种格式来读取%和之间带有星号的字符c

scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3
Run Code Online (Sandbox Code Playgroud)

scanf 然后将读取下一个字符(即换行符)但不将其分配给任何指针。

然而,最后,我会支持常见问题解答的最后一个选项

或者,根据您的要求,您也可以忘记 scanf()/getchar(),使用 fgets() 从用户那里获取一行文本并自行解析。

  • 这种技术的问题在于,如果用户输入“a”,然后输入空格,然后输入换行符,则抑制的字符转换会读取空格并仍然保留换行符。如果用户在您期望输入“a”时输入“supercalifragilisticexpialidocious”,则您需要处理很多额外的字符。您也永远无法判断尾随抑制转换是否成功 - 它们不计入“scanf()”的返回值中。 (4认同)
  • 这只会丢弃*一个*字符。在“%”之前放置一个空格将丢弃*任意数量*的前导空格(包括无)。 (3认同)

Jiw*_*won 7

getchar()在调用 second 之前使用scanf()

scanf("%c", &c1);
getchar();  // <== remove newline
scanf("%c", &c2);
Run Code Online (Sandbox Code Playgroud)

  • @john:它可以是一行、两行、三行或更多——编译器不在乎。留给我自己的设备,它将是三行:声明、循环控制和循环体。表示空循环体的分号将单独占一行,以强调它是故意的(如 K&amp;R 所建议的)。 (3认同)
  • 如果用户没有输入任何其他内容,例如尾随空格,则此方法有效。但它不如扫描到下一个换行符的循环好:`int c; while ((c = getchar()) != EOF &amp;&amp; c != '\n') ;`(不在注释中时写成三行)。这通常就足够了;它不是万无一失的(而且你必须记住,傻瓜在撞车方面非常聪明)。 (2认同)