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