我有以下程序:
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行所示。是不是?
答案 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()