我知道如何编写一个 while 循环来读取用户输入,存储到数组中,然后在停止值处终止。但是,该停止值也被存储,我不知道如何让它不这样做。
这是我所拥有的:
printf("Enter temperatures or -100.0 to stop: "); // user input prompt
int i = 0;
while (1) {
scanf("%f", &temp[i]); // user input filling array
if (temp[i] == -100.0) { // stop loop if input = -100.0
break;
}
i++;
}
scanf()
、man 3 scanf 警告: 的联机帮助页
正确使用这些功能是非常困难的,而且 最好使用
或 getline(3) 读取整行并且 稍后使用fgets(3)
或更专业的函数解析它们,例如 作为sscanf(3)
。strtol(3)
这就是为什么在 StackOverflow 上你会一次又一次地找到 C 语言中用户输入问题的答案,建议
scanf()
不是用户输入的明智选择,使用 fgets()
来阅读整行和从读取的输入行中解析任何需要的值。造成这种情况的原因有很多,但归结为:
stdin
中提取字符将在此时停止,所有导致失败的字符都将留在stdin
中未读,只是等待在您下次尝试输入时咬住您。'\n'
中也会留下一个stdin
字符,只是等待在下一个输入时再次咬你,不会丢弃前导空格(scanf()
说明符"%c"
,"%[..]"
,和 "%n"
不丢弃前导空格,fgets()
或 getline()
- 或任何其他进行输入的系统调用也不丢弃)scanf()
的返回来判断输入和转换是否成功stdin
删除剩余字符后,必须清空输入缓冲区 (scanf()
)。EOF
的返回以及检查是否成功以允许用户取消用户输入 - 这也是有效的输入。使用
fgets()
读取整行用户输入可以避免这些问题,但您仍然需要通过检查 fgets()
返回来进行正确的验证。
除了使用
scanf()
导致的代码问题之外,将 “stopvalue” 包含在 temp[]
数组中的直接问题是因为您使用 scanf()
执行转换为 &temp[i]
,这使得数组中的值,无论它是否是停止值。要解决这个问题,只需声明一个临时变量并用它来保存转换后的值。然后,您可以在将临时值添加到数组之前对照停止值检查临时值。
您遇到的另一个问题是比较停止值的浮点数,例如
if (temp[i] == -100.0)
。距离 -100.0
有多近才算数?您想存储-100.01
吗? -100.04
呢? -99.999
呢?所有这些值都可以比较等于 -100.0
。1 通常选择 Magic-Number 作为停止值会出现问题。 fgets()
也为该问题提供了一个简单的解决方案。 fgets()
不使用幻数,而是可以检查无输入,允许用户单独按 Enter 无需输入即可结束输入。这使得所有值都被视为有效温度,而无需制定一个神奇的温度。
使用scanf()
即使有缺点,如果您检查每个所需的条件并注意提取
stdin
中留下的无关字符,您就可以使 scanf()
用于输入。不建议这样做,但你可以这样做:
#include <stdio.h>
#include <math.h>
#define NTEMPS 100 /* if you need a constant, #define one (or more) */
#define STOPV -100
#define ELIMIT 1.e-4
/* simple function to empty characters left in input stream */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF) {
c = getchar();
}
}
int main (void) {
float temp[NTEMPS] = { 0 }; /* array to hold temps */
int i = 0; /* counter variable */
/* loop only while space remains in array */
while (i < NTEMPS) {
float tmp = 0; /* temporary value for conversion */
printf ("\nEnter temperatures or %d to stop: ", STOPV);
fflush (stdout); /* avoid line-buffering issues */
if (scanf("%f", &tmp) != 1) { /* conversion to float failed */
if (feof (stdin)) { /* check for manual EOF */
puts (" user canceled input.");
return 0;
}
else { /* othewise empty characters that reamin in stdin unread */
fputs (" error: invalid floating-point input.\n", stderr);
empty_stdin();
}
continue; /* get next input */
}
/* check for magic-numer exit condition */
if (fabs(tmp - STOPV) < ELIMIT) {
empty_stdin();
break;
}
temp[i] = tmp; /* store converted value in array */
i++; /* increment counter */
}
/* show output */
puts ("\nRecorded Temps:\n");
for (int j = 0; j < i; j++) {
printf ("temp[%2d] : %.2f\n", j, temp[j]);
}
}
注意: 使用
-100
周围(在 1.e-4
内)的范围被视为您的神奇止损值。
使用fgets()
不仅建议使用
fgets()
进行用户输入,实际上使用 scanf()
进行所需的所有各种检查和输入缓冲区管理更容易。您所做的不是直接使用 scanf()
进行输入,而是声明一个字符数组来保存用户输入,然后将该数组传递给 sscanf()
,而不是直接使用 scanf()
进行输入。它使事情变得更容易(而且更短),例如
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NTEMPS 100
int main (void) {
char buf[MAXC] = ""; /* array to hold line of user-input */
int i = 0; /* counter variable */
float temp[NTEMPS] = { 0 }; /* array to hold temps */
/* loop continually reading input */
while (1) {
float tmp; /* temporary value for conversion */
/* prompt and flush output (no trailing '\n' in prompt) */
fputs ("\nEnter temperatures (empty-input to stop) : ", stdout);
fflush (stdout);
/* read input, check for manual EOF */
if (fgets (buf, MAXC, stdin) == NULL) {
puts (" user canceled input.");
return 0;
}
if (buf[0] == '\n') { /* if Enter pressed with no input */
break; /* done! */
}
/* validate conversion to float */
if (sscanf (buf, "%f", &tmp) == 1) {
temp[i] = tmp; /* add value to temperature array */
i++; /* increment counter */
}
else { /* otherwise conversion failed, show invalid input */
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from string */
fprintf (stderr, " error: invalid floating-point input '%s'.\n",
buf);
}
}
/* show output */
puts ("\nRecorded Temps:\n");
for (int j = 0; j < i; j++) {
printf ("temp[%2d] : %.2f\n", j, temp[j]);
}
}
示例使用/输出
$ ./read-float-arr-fgets
Enter temperatures (empty-input to stop) : 10.8
Enter temperatures (empty-input to stop) : -20.9
Enter temperatures (empty-input to stop) : 59.1
Enter temperatures (empty-input to stop) : 37.4
Enter temperatures (empty-input to stop) : banannas and barbeque sauce
error: invalid floating-point input 'banannas and barbeque sauce'.
Enter temperatures (empty-input to stop) : -100.2
Enter temperatures (empty-input to stop) : -99.99
Enter temperatures (empty-input to stop) :
Recorded Temps:
temp[ 0] : 10.80
temp[ 1] : -20.90
temp[ 2] : 59.10
temp[ 3] : 37.40
temp[ 4] : -100.20
temp[ 5] : -99.99
尝试在程序中输入
"banannas and barbeque sauce"
,看看会发生什么(提示:手边放 ctrl + c)
对于上面的
scanf()
实现,唯一的区别是 stopvalue 行,如下所示:
Enter temperatures or -100 to stop: -100.0001
花时间浏览每个示例并查找所使用的每个函数,并理解为什么需要这样做来创建一个相当强大的输入例程。如果您还有其他问题,请在下面发表评论,我很乐意为您提供进一步帮助。
脚注: