问题 使用scanf()从键盘读取时从上一个输入读取换行符


这应该是非常简单的,但是我无法从键盘读取连续的输入。

这是代码:

#include <string.h>
#include <stdio.h>

int main()
{
    char string[200];
    char character;
    printf ("write something: ");
    scanf ("%s", string);
    printf ("%s", string);
    printf ("\nwrite a character: ");
    scanf ("%c", &character);
    printf ("\nCharacter %c  Correspondent number: %d\n", character, character);

    return 0;
}

发生什么事

当我输入一个字符串(例如:计算机)时,程序会读取换行符('\n'并把它放进去 character。以下是显示的外观:

 write something: computer
 computer
 Character:
    Correspondent number: 10

此外,该程序不适用于包含多个单词的字符串。 我怎么能克服这些问题?


12828
2018-04-27 20:08


起源

scanf 是不安全的,使用 fgets 代替。 - John
@John在这个用例中,是的,但不是一般的! - fuz
可能重复 为什么Scanf在拍摄角色时工作很奇怪 - ericbn
@ElCid,当然! meta.stackoverflow.com/questions/252929/... - ericbn
关于这一行:'scanf(“%s”,string);'用户可能会溢出输入缓冲区,导致未定义的行为并导致seg故障事件。建议:'scanf(“%199s”,string);' - user3629249


答案:


第一 scanf 读取输入的字符串并留下 \n 在输入缓冲区中。接下来打电话给 scanf 看了 \n 并存储它 character
尝试这个

scanf (" %c", &characte);   
     // ^A space before %c in scanf can skip any number of white space characters. 

程序不适用于多个字符串的字符串,因为 scanf 一旦找到白色空间字符就停止阅读。您可以使用 fgets 代替

 fgets(string, 200, stdin);

8
2018-04-27 20:13



OP的使用 printf() 是正确的。变元论证如 char character 使用通常比例晋升 int (或很少 unsigned)所以演员 int 不需要。 printf ("%c %d",... 需要两个 int 参数,所以OP使用正确的说明符。 “我,我 int 参数被转换...“”c如果不存在l长度修饰符,则 int 论证......“C11§7.21.6.18 - chux
@chux;好点子。今天学到了! - haccks
我同意使用fgets()而不是scanf()来读取一行文本。但是,第二个输入预计会输入'换行符'而'换行符'是空格,所以建议:scanf(“c”,&character);将无法正常工作,因为scanf将跳过新行。也许这就是想要的,关于这个问题的问题还不清楚。 - user3629249
@ user3629249;它总是安全使用 " %c" 在 scanf。如果用户按两次Enter / Return会怎样? - haccks


OP的第一个问题通常是通过在格式前加一个空格来解决。这将占用包括前一行的空白空间 '\n'

// scanf("%c", &character);
scanf(" %c", &character);

此外,该程序不适用于包含多个单词的字符串。我怎么能克服这些问题?

对于第二个问题,让我们更准确地理解“字符串”和什么 "%s" 确实。

一个  是由第一个空字符终止并包括第一个空字符的连续字符序列。 7.1.1 1

即使报告“我输入一个字符串(例如:计算机)”,OP也没有输入字符串。 OP正在输入一行文字。 8个字符“计算机”后跟 输入。这里没有“空字符”。相反9 char  "computer\n"

"%s" 在 scanf("%s", string); 做3件事:

1)扫描,但不保存任何前导空白区域。

2)扫描并保存到 string 任意数量的非白色空间。

3)到达白色空间或EOF时停止扫描。那 char 但是又回来了 stdin。一个 '\0' 附加到 string 做到这一点 char 数组一个C.

要读取包含空格的行,请不要使用 scanf("%s",...。考虑 fgets()

fgets(string, sizeof string, stdin);
// remove potential trailing \r\n as needed
string[strcspn(string, "\n")] = 0;

混合 scanf() 和 fgets() 这是一个问题 scanf("%s", string); fgets(...) 离开了 '\n' 在 stdin 对于 fgets() 读作只包含的一行 "\n"。建议改为阅读 所有 用户输入使用 fgets() (要么 getline() 在* nix系统上)。 然后 解析读取的行。

fgets(string, sizeof string, stdin);
scanf(string, "%c", &character);

如果代码 必须 用户 scanf() 读取包含空格的用户输入:

scanf("%*[\n]"); // read any number of \n and not save.
// Read up to 199 `char`, none of which are \n
if (scanf("%199[^\n]", string) != 1) Handle_EOF();

最后,代码应该使用错误检查和输入宽度限制。测试所有输入函数的返回值。


3
2018-04-27 22:00





您所看到的是您调用的函数的正确行为:

  • scanf会读  输入中的字,并在输入的字后立即保留输入指针。如果你输入 computer<RETURN>,要读取的下一个字符是换行符。

  • 要阅读整行,包括最终换行,请使用 fgets。仔细阅读文档: fgets 返回一个字符串,其中包含它读取的最终换行符。 (gets,由于多种原因不应该使用,读取并丢弃最终换行符。)

我应该补充一点 scanf 有它的用途,使用它交互式导致 非常 令人困惑的行为,我认为你发现了。即使在您想要逐字阅读的情况下,如果预期用途是交互式的,也可以使用其他方法。


2
2018-04-27 20:29



谢谢,在你的提示下,我放弃了默示的支持。有足够的理由避免 gets 甚至在罐装之前...... - alexis


你可以利用 %*c

#include <string.h>
#include <stdio.h>

int main()
{
    char string[200];
    char character;
    printf ("write something: ");
    scanf ("%s%*c", string);
    printf ("%s", string);
    printf ("\nwrite a character: ");
    scanf ("%c%*c", &character);
    printf ("\nCharacter %c  Correspondent number: %d\n", character, character);

    return 0;
}

%*c 将接受并忽略换行符或任何空格


0
2018-04-27 21:42



注意: %*c 将接受并忽略任何以下内容 char 这通常是换行符,但可能是任何白色空间 ' '。 - chux
@ chux,是的,没错。我会准确地将你的笔记添加到我的答案中。 - Jahid
这有一个缓冲区溢出;你需要限制输入 - M.M


你也放了 getchar() 之后 scanf 线。它会做的工作:)


0
2018-03-04 21:57





流需要刷新。执行连续输入时,标准输入流stdin缓冲键盘上的每次按键。因此,当您键入“计算机”并按下回车键时,输入流也会吸收换行,即使只将字符串“computer”分配给 string。因此,当您稍后扫描某个字符时,已加载的新行字符是扫描并分配给该字符的字符 character

还需要刷新stdout流。考虑一下:

...
printf("foo");
while(1)
{}
...

如果尝试执行类似这样的操作,则控制台上不会显示任何内容。系统缓冲了 stdout stream,标准输出流,不知道接下来会遇到无限循环的事实,一旦发生这种情况,它永远不会有机会将流卸载到控制台。

显然,每当有类似的方式 scanf 阻止程序并等待 stdin,标准输入流,它影响其他缓冲流。无论如何,无论如何,如果事情开始笨拙,最好能够正确地冲洗流。

对代码的以下修改似乎产生了所需的输出

#include <string.h>
#include <stdio.h>

int main()
{
char string[200];
char character;

printf ("write something: ");

fflush(stdout);

scanf ("%s", string);

fflush(stdin);

printf ("%s", string);
printf ("\nwrite a character: ");

fflush(stdout);

scanf ("%c", &character);
printf ("\nCharacter %c  Correspondent number: %d\n", character, character);

return 0;
} 

产量
写点东西:电脑
电脑
写一个字符:a
 

字符对应号码:97


-1
2018-02-10 00:42



你的代码有缓冲区溢出,和 fflush(stdin) 导致未定义的行为 - M.M
@ M.M任何证据都支持fflush产生未定义的行为这一事实!而且我没有完全得到缓冲区溢出部分,请你重新说明/解释。谢谢。 - ps_
如果他们输入200个字母,那么你就会溢出 string。对于 fflush 请参阅当前C标准的第7.21.5.2/2节,“如果流指向输入流或未输入最近操作的更新流,则fflush函数会将该流的任何未写入数据传送到要写入文件的主机环境;否则,行为未定义“。 stdin 不是输出或更新流。 - M.M
@ M.M就缓冲区溢出而言,这不是我想要做的。显然,如果您尝试存储超出其容量的数据,则数组会溢出。我既没有解决这个问题,也没有尝试在我的代码中适应这个问题。 - ps_
@ M.M是的,规范是这样说的,但仍然如此 fflush 表现完全像人们预测的那样,甚至假设输入流也是如此,因此我认为这样做是安全的 stdin 可以以这种方式刷新而不会产生任何影响,除非你有一个反例,在连续输入之间刷新stdin会导致不确定的行为。另外我不太清楚术语更新流,你能否告诉我它究竟是什么意思,以及输入流和更新流之间的区别是什么。谢谢。 - ps_