Fgets 跳过第一个字符

Fgets skip first character

提问人:Dat Tran 提问时间:4/30/2023 最后编辑:chqrlieDat Tran 更新时间:5/1/2023 访问量:87

问:

当我将用户名作为输入并使用该信息执行另一项任务时,输出不包含第一个学生姓名的第一个字符。

int main() {
    // Task1 - Enter student number
    int i, number;
    printf("Enter the number of students: ");
    scanf("%d", &number);
    getchar();
    while (number > 1000) {
        printf("Your number is too high. Maximum number of student is 1000. Please enter again: ");
        scanf("%d", &number);
    }
    //Task2 - get student info
    struct studentInfo students[number];
   
    for (i = 0; i < number; i++) {
        printf("\nEnter data for student %d:\n", i + 1);
        printf("Enter student %d name: ", i + 1);
        getchar(); // consume the newline character left in the input stream
        fgets(students[i].fullName, sizeof(students[i].fullName), stdin); //use fgets instead of scanf
        students[i].fullName[strcspn(students[i].fullName, "\n")] = 0; // Remove trailing newline
 
        printf("Enter student %d ID: ", i + 1);
        scanf("%s", students[i].ID);
       
        printf("Enter student %d birthdate: ", i + 1);
        scanf("%s", students[i].birthDate);
    }
}

我已经更改并添加,但它仍然不起作用fgetsgetsgetchar

C 键盘 扫描 fgets getchar

评论

0赞 Andreas Wenzel 5/1/2023
寻求调试帮助的问题通常应提供问题的最小可重现示例,其中包括重现问题所需的确切输入的所有指令,在本例中,还包括 的定义。这允许其他人通过简单地使用复制和粘贴来轻松测试您的程序。#includestruct studentInfo
0赞 Andreas Wenzel 5/1/2023
在你的问题中,你写道: -- 什么输出?您发布的代码不包含任何打印第一个学生姓名的尝试。请提供问题的最小可重现示例,并准确指定输入、期望输出和实际输出。"the output does not contains first character of the first student name"
0赞 Joshua 5/1/2023
@AndreasWenzel:我发现有足够的信息来回答。因此,我尽可能地以一种即使是新手程序员也可以应用的形式回答;虽然教师可能会问.sscanf()
0赞 Andreas Wenzel 5/1/2023
@Joshua:是的,我同意这个问题已经可以回答了(实际上我自己也回答了这个问题),如果你用猜测来填补所有的空白。例如,为了回答问题,有必要推测它是一个数组而不是指针,并且它有足够的大小来容纳输入。还需要推测所有必要的指令都被使用,以便编译器不使用任何隐式声明(这可能会导致编译器对参数类型或返回值做出错误的假设)。students[i].fullName#include

答:

2赞 Joshua 4/30/2023 #1

问题是在 之后,输入流中留下了一个换行符。使用 用于键盘输入是一个坏主意。请考虑改用 and。scanf("%d", ...)scanf()fgets()sscanf()

例:

    printf("Enter the number of students: ");
    scanf("%d", &number);

将变为:

    printf("Enter the number of students: ");
    fgets(keyboard_line, 80, stdin);
    sscanf(keyboard_line, "%d", &number);

您在顶部声明的位置keyboard_linechar keyboard_line[80];main();

评论

0赞 Dat Tran 4/30/2023
我是否必须删除该部分之后的 getchar 行并在 (数字 > 1000) 时进行编辑?
0赞 Joshua 4/30/2023
@DatTran:是的。最好的方法显然是使用 fgets() 一次读取所有键盘输入行。
0赞 Dat Tran 4/30/2023
我的任务 2 中有什么错误吗?请让我知道,谢谢。
0赞 Andreas Wenzel 5/1/2023
在你的回答中,你写道: -- 这个说法是不正确的,因为 OP 通过使用 成功地丢弃了该换行符。因此,这不是“问题”。问题在于,OP 的代码在循环的第一次迭代中调用了太多次。有关更多信息,请参阅我的回答。但是,您提供的解决方案仍将解决问题。"The problem is after scanf("%d", ...), a newline is left in the input stream."getchar();getchar();fgets
1赞 chux - Reinstate Monica 5/2/2023
@Joshua,未成年人:最好作为char keyboard_line[80]; ... fgets(keyboard_line, sizeof keyboard_line, stdin);
2赞 Andreas Wenzel 5/1/2023 #2

问题是语句

scanf("%d", &number);

将在输入流上保留换行符,而语句

fgets(students[i].fullName, sizeof(students[i].fullName), stdin);

将读取包含换行符的整行输入(假设提供的内存缓冲区足够大,可以存储整行)。

您似乎试图通过使用语句来补偿在输入流上留下换行符的事实scanf

getchar();

在代码中的多个位置读取和丢弃换行符。但是,您没有正确地执行此操作。

如果用户在下面为语句输入数字1000

scanf("%d", &number);

然后你将执行该语句

getchar();

之后两次,因为该语句在您发布的代码中的两个位置。这意味着它可能会读取换行符以及用户下一行输入的第一个字符。

这就解释了为什么在第一个循环迭代中,缺少第一个字符。students[i].fullName

为了解决这个问题,我建议你总是有这个声明

getchar();

  1. 在每次函数调用后立即,或者scanf
  2. 在每次函数调用之前。fgets

您发布的代码中的问题是您正在混合执行两者。

请注意,解决方案 #2 仅在程序中的第一个语句是语句时才有效,因为如果第一个语句是语句,则没有要删除的换行符。出于这个原因,使用解决方案#1可能会更好。scanffgets

此外,使用语句

getchar();

仅当仅保留换行符时才有效。但是,如果用户输入scanf

6abc

则 的格式说明符将只匹配 ,但将保留在输入流上。在这种情况下,有必要呼叫四次而不是一次。%dscanf6abc\ngetchar()

因此,通常最好改用以下循环:

int c;

do
{
    c = getchar();

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

此循环可以缩短为以下两行:

for ( int c; ( c = getchar() ) != EOF && c != '\n' )
    ;

如果你在程序中多次使用它,那么简单地编写你自己的函数可能会更容易:

void discard_remainder_of_line()
{
    int c;

    do
    {
        c = getchar();

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

这样,你只需要写

discard_remainder_of_line();

在每句话之后。scanf

在使用 的结果之前始终检查 的返回值也会更安全。scanfscanf

评论

0赞 chux - Reinstate Monica 5/2/2023
当然,两者的功能都是一样的,但是由于更有可能,首先测试会更快一些。while ( c != EOF && c != '\n' );while ( c != '\n' && c != EOF);'\n'