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

时间:2011-03-09 02:56:03

标签: 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;
}

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

是吗?

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

5 个答案:

答案 0 :(得分:54)

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

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

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


请注意,转换以外的非空白“指令”(使用POSIX scanf terminology),例如scanf("order = %d", &order);中的文字文本也不会跳过空格。文字order必须匹配要读取的下一个字符。

因此,如果您想跳过上一行的换行符,但仍需要固定字符串like this question上的文字匹配,那么您可能需要" order = %d"

答案 1 :(得分:27)

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

答案 2 :(得分:3)

为了回应我在 another answer about C++ 中发布的内容:我建议将 scanf() 扔掉,永远不要使用它,而是使用 fgets()sscanf() (*).

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

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

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

printf("Enter a word: ");
scanf("%s", word);

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

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

相反,他们希望按回车完成输入,如果您按回车,您要么得到一个默认值,要么得到一个错误,可能还有一个建议,请真正给出答案。

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

这也不是很有用。

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

(fscanf() 在普通文件上也可以,如果文件格式支持它如何像任何空格一样跳过换行符。对于面向文件的数据,我仍然使用 {{ 1}} 和 fgets()。)


因此,不要使用我上面的内容,而是使用以下内容:

sscanf()

或者,实际上,也检查 printf("Enter a number: "); fgets(buf, bufsize, stdin); sscanf(buf, "%d", &a); 的返回值,以便我们可以检测空行或其他无效数据:

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() 之后的换行符返回,从而清除缓冲区中剩余的任何垃圾,但这对用户只是按 Enter 键的情况没有任何作用空行。无论如何,scanf("%d") 会一直读到换行符,所以您不必自己做。

答案 3 :(得分:1)

另一种选择(我从here获得)是通过使用 assignment-supression选项读取和丢弃换行符的。为此,我们只需要一种格式来读取在jupyter notebook --NotebookApp.iopub_data_rate_limit=<LARGE_NUMBER> %之间带有星号的字符:

c

scanf("%d%*c",&a); // line 1 scanf("%c%*c",&c1); // line 3 然后将读取下一个字符(即换行符),但不会将其分配给任何指针。

但是最后,我将第二次the FAQ's last option

  

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

答案 4 :(得分:0)

在呼叫第二个class CApplicative f where type C f pure :: C f a => a -> f a app :: C f b => f (a -> b) -> f a -> f b instance CApplicative Pair where type C Pair = Semigroup pure x = Pair x x app (Pure f g) p = fmap f p <> fmap g p 之前使用getchar()

scanf()