为什么这个scanf格式字符串不起作用?"%[^ \n]的\n" 个

iva*_*van 5 c input scanf

我已经看到一些例子,人们给scanf一个"%[^\n]\n"格式字符串来读取整行用户输入.如果我的理解是正确的,这将读取每个字符,直到到达换行符,然后新行被scanf消耗(并且不包括在结果输入中).

但我不能让这个在我的机器上工作.我试过的一个简单例子:

#include <stdio.h>

int main(void)
{
    char input[64];

    printf("Enter some input: ");
    scanf("%[^\n]\n", input);

    printf("You entered %s\n", input);
}
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我被提示输入,我输入一些字符,我按Enter键,光标转到下一行的开头,但scanf调用没有完成.

scanf没有完成

我可以随心所欲地按Enter键,但它永远不会完成.

scanf仍未完成

我发现结束scanf调用的唯一方法是:

  • \n在提示符下输入第一个(也是唯一的)字符
  • 输入Ctrl-d作为提示符下的第一个(也是唯一的)字符
  • 输入一些输入,一个或多个\n,零个或多个其他字符,然后输入Ctrl-d

我不知道这是否依赖于机器,但我很想知道发生了什么.我在OS X上,如果那是相关的.

Ilm*_*nen 6

根据scanf(强调我的)的文档:

格式字符串由空格字符组成(格式字符串中的任何单个空白字符都使用输入中的所有可用连续空白字符),非空白字节数字除外%(格式字符串中的每个此类字符仅消耗输入中的一个相同字符)和转换规范.

因此,您的格式字符串%[^\n]\n将首先从输入中读取(并存储)任意数量的非空白字符(因为该%[^\n]部分),然后,由于以下换行符,读取(并丢弃)任意数量的空白字符,例如空格,制表符或换行符.

因此,要使scanf停止读取输入,您需要在换行符后键入至少一个非空白字符,或者安排输入流结束(例如,在Unix-ish系统上按Ctrl+ D).

相反,要使代码按预期工作,只需\n从格式字符串末尾删除最后一个(如Umamahesh P已经建议的那样).

当然,这将使换行符仍然在输入流中.要摆脱它(如果你想稍后读取另一行),你可以将它从流中取出,或者只是追加%*c(意思是"读取一个字符并丢弃它")或者甚至%*1[\n](读取一个换行并丢弃它)到您的scanf格式字符串的末尾.

PS.请注意,您的代码还有其他一些问题.例如,为了避免缓冲区溢出错误,您应该使用%63[^\n]而不是%[^\n]限制scanf将读入缓冲区的字符数.(限制需要比缓冲区大小小一个,因为scanf将始终附加一个尾随空字符.)

此外,%[格式说明符始终需要至少一个匹配字符,如果没有可用,则会失败.因此,如果您在没有输入任何内容的情况下立即按Enter键,您的scanf将失败(默认情况下,因为您不检查返回值)并且将使您的输入缓冲区充满随机垃圾.为了避免这种情况,你应该a)检查scanf的返回值,b)input[0] = '\0'在调用scanf之前设置,或c)最好同时检查两者.

最后,请注意,如果您只想逐行读取输入,则使用fgets 容易得多.是的,如果你不想要它,你需要自己去除尾随的换行符(如果有的话),但是尝试将scanf用于一个并不真正意味着的工作仍然更容易和更安全:

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

void chomp(char *string) {
    int len = strlen(string);
    if (len > 0 && string[len-1] == '\n') string[len-1] = '\0';
}

int main(void)
{
    char input[64];

    printf("Enter some input: ");
    fgets(input, sizeof(input), stdin);
    chomp(input);

    printf("You entered \"%s\".\n", input);
}
Run Code Online (Sandbox Code Playgroud)