如何在循环中使用fgets和sscanf作为整数

时间:2013-01-23 03:31:50

标签: c fgets scanf

这里有C的初学者。我正在尝试运行一个循环,其中字符串和ints被输入到struct的各个字段中。当提示输入“姓氏”时,用户可以在没有其他输入的情况下按Enter键,循环结束。

问题在于,使用此代码,循环不会结束(姓氏和名字条目请求在同一行上一起运行)并且薪水的值总是出错(0或某个大数字)

while (employee_num <= 2)
{
    printf("Enter last name ");
    fgets(employee[employee_num].last_name, sizeof(employee[employee_num].last_name), stdin);                   

    if(strlen(employee[employee_num].last_name) == 0)
        break;

    printf("Enter first name ");
    fgets(employee[employee_num].first_name, sizeof(employee[employee_num].first_name), stdin);

    printf("Enter title ");
    fgets(employee[employee_num].title, sizeof(employee[employee_num].title), stdin);

    printf("Enter salary ");
    fgets(strng_buffer, 1, stdin);
    sscanf(strng_buffer, "%d", &employee[employee_num].salary);     
    ++employee_num;
    getchar();
}

如果我尝试使用此代码,我可以在第一次运行后正确退出循环,但在此之后无法退出(通过在姓氏部分按Enter键 - 也许是\ n我似乎无法清除? ):

char strng_buffer[16];
while (employee_num <= 5)
{
    printf("Enter last name ");
    fgets(strng_buffer, sizeof(strng_buffer), stdin);                   
    sscanf(strng_buffer, "%s", employee[employee_num].last_name);       

    if(strlen(employee[employee_num].last_name) == 0)
        break;

    printf("Enter first name ");
    fgets(strng_buffer, sizeof(strng_buffer), stdin);
    sscanf(strng_buffer, "%s", employee[employee_num].first_name);


    printf("Enter title ");
    fgets(strng_buffer, sizeof(strng_buffer), stdin);
    sscanf(strng_buffer, "%s", employee[employee_num].title);

    printf("Enter salary ");
    scanf("%d", &employee[employee_num].salary);        
    ++employee_num;
    getchar();
}

我很好奇如何使这项工作按预期进行,以及对此类条目的最佳实践(即使用sscanf,fgets等)

提前致谢!

3 个答案:

答案 0 :(得分:2)

当Loop遇到break语句

时,它会过早地中断
if(strlen(strng_buffer) == 0)
        break;

未初始化的字符缓冲区strng_buffer巧合地将null作为导致strlen返回0的第一个字符

我相信你可能有意

if(strlen(employee[employee_num].last_name) == 0)
            break;

作为循环终止符,并且在您的部分中是一个错字导致过早的循环退出。

答案 1 :(得分:1)

问题是fgets返回包含换行符(\n)的字符串。因此,即使用户在不输入信息的情况下按下返回,该字符串也不会为空。此外,salary的缓冲区大小太小。

因此,要么在每个\n上删除fgets,要么将支票更改为:

if(strlen(employee[employee_num].last_name) == 1) break;

此外,当您获得缓冲区时,请将1更改为更大的内容,例如

fgets(strng_buffer, 10, stdin);

但是,如果你想从每个fgets中删除\n,你可以执行以下操作:

employee[employee_num].last_name[strlen(employee[employee_num].last_name)-1] = 0;

您可以为每个字符串执行此操作,或者更好的是,创建一个执行该操作的函数。

编辑:如果您可以保证用户在每次输入后按Enter键,那么您可以放心地假设这一点。但是,如果情况并非总是如此,那么最后一个字符可能不是\n,只是以这种方式剥离可能会导致问题。

答案 2 :(得分:1)

假设Abhijit提到的修复,为什么要将第一个转换为第二个?您是否意识到第二种行为与第一种行为不同,因为添加了sscanf?如果你打算缩短第一个,那么第二个看起来很笨重。不是将sscanf添加到情境中,为什么不通过声明struct employee *e = employee + employee_num;并重复使用,而不是employee[employee_num]来缩短第一个?

关于fgets的一个“最佳做法”是检查它的返回值。如果遇到EOF,您认为fgets可能会返回什么?如果成功,你认为fgets会返回什么?

关于scanf的一个“最佳做法”是检查它的返回值。关于scanf的返回值,我建议您仔细阅读this scanf manual并回答以下问题:

  1. int x = scanf("%d", &employee[employee_num].salary);如果我输入x作为输入,您认为"fubar\n"是什么意思?
  2. 您认为来自'f'的{​​{1}}会去哪儿?
  3. 如果"fubar\n"返回ungetc,您下一个员工的姓氏会是什么?
  4. stdin如果我在Windows上运行此代码并按CTRL + Z将int x = scanf("%d", &employee[employee_num].salary);发送到x,您认为EOF是什么?
  5. stdin您期望int x = scanf("%d %d", &y, &z);期待什么,假设x成功地将值放入两个变量scanfy
  6. P.S。 z可以通过CTRL + Z在Windows中通过EOF发送,也可以通过CTRL + D通过Linux和朋友发送,除了使用管道和重定向来重定向来自其他程序和文件的输入。