【问题标题】:Why is scanf(); skipping the first line?为什么是 scanf();跳过第一行?
【发布时间】:2020-05-09 15:34:24
【问题描述】:

所以在 scanf 之后,printf(); 会跳过第一行
我读过一些问题,告诉"%[^\n]" 必须是" %[^\n]" 才能跳过换行符。
我都试过了,但它仍然打印相同的结果,现在我不知道为什么它不起作用。

Example input
Enter number of Materi: 4
Materia 1 name : a
Materia 2 name : b
materia 3 name : c
materia 4 name : d

Output:
Materia - 1 : R╒fu
Materia - 2 : a
Materia - 3 : b
Materia - 4 : c
#include<stdio.h>
#include<windows.h>

int main(int argc, const char *argv[]){

    int i;
    int V;

    printf("Enter number of Materi: ");
    scanf("%d", &V); fflush(stdin);

    //Insert materia
    char materia[V][50];
    for(i = 0; i < V; i++){
        printf("Materia %d name : ", i+1);scanf("%[^\n]", &materia[i][50]);fflush(stdin);
    }

    for(i = 0; i < V; i++){
        printf("Materia - %d: %s\n", i+1, materia[i]);
    }
    system("pause");

    return 0;
}


【问题讨论】:

  • fflush(stdin) 在 c 中是未定义的行为,在 POSIX 或 Windows 定义的情况下无用。 scanf("%[^\n]", &amp;materia[i][50]) 并没有按照你的想法去做。
  • @EOF 它实际上要求在我使用 fflush(stdin); 后进行扫描; ,当我不使用它时,它甚至根本不会扫描
  • 你没有(合理地)控制你的输入。你可以控制你的输出。
  • "%[^\n]" 添加一个空格,使其变为" %[^\n]",这将过滤前导空格。这种格式(或%c)不是自动的。
  • 还有一个错误:&amp;materia[i][50]应该是materia[i]

标签: c string scanf


【解决方案1】:

程序有几个错误

  • 传递给scanf的数组错误。

  • fflush(stdin) 是非标准的,尽管 Windows 确实支持它,但它不是可移植的。但是您没有使用 Windows,因为它不支持 VLA char materia[V][50];

  • scanf 格式 "%[^\n]" 将停止的换行符已经是输入缓冲区中的下一个字符。

  • 未检查来自scanf 的返回值。它是成功扫描的项目数。

  • 由于输入字符串长度不受限制,可能会导致缓冲区溢出。

调整后的代码如下:

#include<stdio.h>

int main(int argc, const char *argv[]){

    int i;
    int V;

    printf("Enter number of Materi: ");
    if(scanf("%d", &V) != 1) {
        /* add some error message */
        return 1;
    }
    // fflush(stdin);               // removed

    //Insert materia
    char materia[V][50];
    for(i = 0; i < V; i++){
        printf("Materia %d name : ", i+1);
        // add a space to filter the newline
        // correct the array passed
        // and restrict the length to prevent buffer overflow
        if(scanf(" %49[^\n]", materia[i]) != 1) {
            /* add some error message */
            return 1;
        }
        // fflush(stdin);           // removed
    }

    for(i = 0; i < V; i++){
        printf("Materia - %d: %s\n", i+1, materia[i]);
    }

    return 0;
}

关于换行符。格式规范 %d%s%f 会自动过滤掉前导空格,但 %c%[]%n 不会。 scanf 函数在它们无法转换的第一个字符处停止,该字符留在输入缓冲区中。 %[^\n] 告诉它在第一个换行符处停止。但是已经有一个,从第一个%d scanf 开始,它需要被删除,在随后的迭代中也需要添加空间来完成这项工作。之后尝试将其删除是笨拙的,并且不能保证正常工作。

每次使用时都必须检查来自scanf 的返回值。它是成功扫描的项目数。在这里,两种用法都应该是1。所以如果你在一个语句中有两个项目,它的返回值必须是2

【讨论】:

  • scanf("%d", &amp;V); -> 也许检查返回值?
  • 我会把“理想”改为“你应该”
【解决方案2】:

行内:

scanf("%[^\n]", &materia[i][50]); 

您将输入的字符串存储在materia[i][50] 的地址中,有效地将其存储在数组边界之外。

这里发生了一些有趣的事情,二维数组存储是连续的,就好像它是一个一维向量一样,发生的情况是您将第一个字符串存储在数组第二行的开头,第二个字符串存储在第三行,依此类推,将第一个留空。这就是程序产生的输出的合理性。

更正你的代码:

#include<stdlib.h>

//...

if(scanf(" %49[^\n]", materia[i] != 1) {
   puts("Read error");
   return EXIT_FAILURE;       
}

49 个字符 + nul 终止符以避免溢出,说明符开头的空格避免 stdin 缓冲区中剩余的空白字符的消耗。始终验证scanf 返回以避免读取错误。

其他一些问题:

【讨论】:

    【解决方案3】:

    使用fgets 代替scanf。换行符被您后续的scanf 用作一个字符。这就是你面临这个问题的原因。

    【讨论】:

    • 有没有办法仍然使用scanf
    • 如果你想继续使用scanf,你可以调用getchar()来消耗换行符。
    • “这就是你面临这个问题的原因。”剧透:不是。
    • @EOF,需要详细说明一下剧透吗?
    • 你需要这两个更改,它们是不相关的。
    猜你喜欢
    • 2021-11-24
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-02
    • 2021-03-19
    • 1970-01-01
    • 2012-05-21
    相关资源
    最近更新 更多