scanf和getchar的用法

3

在C语言中,以下模式用于获取一个字符串直到换行符是否正确?

int n = scanf("%40[^\n]s", title);
getchar();

它似乎是一种快速去除末尾换行符的方法,但我想知道这里是否存在我没有看到的缺点。

3个回答

3
提交的代码存在多个问题:
  • 格式字符串中的 s 并不是你想象中的那样: 规定是 %40[^\n],而 s 会尝试匹配输入流中的一个 s,这可能发生在已经将 40 个字节存储到 title 中之后。

  • 如果待转换的输入流是一个换行符,则 scanf() 将无法进行转换,将导致 title 未改变并潜在地未初始化。

  • getchar() 不一定读入换行符: 如果该行上有超过 40 个字符,则只会读取下一个字符。

如果要读取一行,最多 40 个字节并忽略其余直到包括换行符的行,请使用以下代码:

    char title[41];
    *title = '\0';
    if (scanf("%40[^\n]", title) == EOF) {
        // end of file reached before reading anything, handle this case
    } else {
        scanf("%*[^\n]"); // discard the rest of the line, if any
        getchar();        // discard the newline if any (or use scanf("%1*[\n]"))
    }

把它写得更易读可能会更好:

    char title[41];
    int c, len = 0;
    while ((c = getchar()) != EOF && c != '\n') {
        if (len < 40)
            title[len++] = c;
    }
    title[len] = '\0';
    if (c == EOF && len == 0) {
        // end of file reached before reading a line
    } else {
        // possibly empty line of length len was read in title
    }

你还可以使用 fgets()

    char title[41];

    if (fgets(title, sizeof title, stdin) {
        char *p = strchr(title, '\n');
        if (p != NULL) {
            // strip the newline
            *p = '\0';
        } else {
            // no newline found: discard reamining characters and the newline if any
            int c;
            while ((c = getchar()) != EOF && c != '\n')
                continue;
        }
    } else {
        // at end of file: nothing was read in the title array
    }

谢谢,那么有更好的方法来做这个模式吗? - David542
@David542:我更新了答案,并添加了3个代码示例。 - chqrlie

1

之前的注释中,应该删除s,它不是格式说明符的一部分,足以破坏你的读取。当你输入超过40个字符的字符串时,scanf将尝试将一个s字符与其匹配,直到找到一个匹配为止,执行才会继续。

用单个getchar回答你的问题并不是最好的方法,你可以使用这个常见的例程来清除缓冲区:

int n = scanf(" %40[^\n]", title);

int c;
while((c = getchar()) != '\n' && c != EOF){}

if(c == EOF){
   // in the rare cases this can happen, it may be unrecoverable
   // it's best to just abort
   return EXIT_FAILURE;
}
//...

这有什么用?它会读取并丢弃stdin缓冲区中剩余的所有字符,无论它们是什么。
当输入字符串具有45个字符时,采用这种方法将清除stdin缓冲区,而单个getchar只会清除1个字符。
请注意,我在说明符前添加了一个空格,这很有用,因为它会在找到第一个可解析字符之前丢弃所有空格,包括换行符、空格、制表符等。这通常是期望的行为,例如,如果您按下Enterspace Enter,它将丢弃这些内容并继续等待输入,但如果您想解析空行,则应删除它,或者使用fgets

谢谢,你能解释一下我使用的方法为什么不好吗?还有,你需要在 getchar 周围加上 () 吗? - David542
好的 -- 那个 getchar 呢?难道不应该是 getchar() 吗? - David542
@David542,是的,打错了。 - anastaciu
@chqrlie 是的,没错,通常可以把它视为一个奖励,但它也可能成为一个问题。 - anastaciu
@chqrlie,通常最简单的解决方案可能是使用fgets - anastaciu
显示剩余5条评论

0

你的代码存在一些问题,比如n从未被使用,以及scanf的错误指示符。

更好的方法是使用fgetsfgets也会读取换行符(如果在缓冲区满之前存在),但很容易去除。

请参见从fgets()输入中删除尾随换行符


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接