创建更友好的用户界面
使用缓冲输入
/* guess.c */
#include <stdio.h>
int main(void)
{
int guess = 1;
printf("Pick an integer from 1 to 100. I will try to guess it\n");
printf("Respond with a y if my guess is right and with a n if it is wrong.\n");
printf("Uh...is your number %d?\n", guess);
while (getchar() != 'y')
{
printf("Well, then, is it %d?\n", ++guess);
}
printf("I knew I could do it\n");
return 0;
}
mali@mali:~/code/file$ gcc guess.c -o guess
mali@mali:~/code/file$ ./guess
Pick an integer from 1 to 100. I will try to guess it
Respond with a y if my guess is right and with a n if it is wrong.
Uh...is your number 1?
n
Well, then, is it 2?
Well, then, is it 3?
n
Well, then, is it 4?
Well, then, is it 5?
y
I knew I could do it
mali@mali:~/code/file$
可以看到,每次输入n时,程序打印了两条消息。这是由于程序读取n作为用户否定了数字1,然后还读取了一个换行符作为用户否定了数字2.
一种解决方案是:使用while循环丢弃输入行最后剩余的内容,包括换行符。这种方法的优点是:能把no和no way这样的响应视为简单的n。
/* guess.c */
#include <stdio.h>
int main(void)
{
int guess = 1;
printf("Pick an integer from 1 to 100. I will try to guess it\n");
printf("Respond with a y if my guess is right and with a n if it is wrong.\n");
printf("Uh...is your number %d?\n", guess);
while (getchar() != 'y')
{
printf("Well, then, is it %d?\n", ++guess);
while (getchar() != '\n')
continue;
}
printf("I knew I could do it\n");
return 0;
}
mali@mali:~/code/file$ gcc guess1.c -o guess1
mali@mali:~/code/file$ ./guess1
Pick an integer from 1 to 100. I will try to guess it
Respond with a y if my guess is right and with a n if it is wrong.
Uh...is your number 1?
n
Well, then, is it 2?
no
Well, then, is it 3?
no sir
Well, then, is it 4?
forget it
Well, then, is it 5?
y
I knew I could do it
mali@mali:~/code/file$
这的确解决了换行符的问题。但是,程序还是会把fp视为n。
我们用if语句筛选其他响应。
/* guess1.c */
#include <stdio.h>
int main(void)
{
int guess = 1;
char response;
printf("Pick an integer from 1 to 100. I will try to guess it\n");
printf("Respond with a y if my guess is right and with a n if it is wrong.\n");
printf("Uh...is your number %d?\n", guess);
while ((response = getchar()) != 'y')
{
if (response == 'n')
printf("Well, then, is it %d?\n", ++guess);
else
printf("Sorry, I understand only y or n.\n");
while (getchar() != '\n')
continue;
}
printf("I knew I could do it\n");
return 0;
}
mali@mali:~/code/file$ ./guess2
Pick an integer from 1 to 100. I will try to guess it
Respond with a y if my guess is right and with a n if it is wrong.
Uh...is your number 1?
n
Well, then, is it 2?
no
Well, then, is it 3?
no sir
Well, then, is it 4?
forget it
Sorry, I understand only y or n.
n
Well, then, is it 5?
y
I knew I could do it
mali@mali:~/code/file$
在编写交互式程序时,应该事先预料到用户可能会输入错误,然后设计程序处理用户的错误输入。在用户出错时通知用户再次输入。
混合数值和字符输入
假设程序要求用getchar()处理字符输入,用scanf()处理数值输入,这两个函数都能很好地完成任务,但是不能把它们混用。因为getchar()读取每个字符,包括空格、制表符和换行符,而scanf()在读取数字时会跳过空格、制表符和换行符。
该程序读入一个字符和两个数字,然后根据输入的两个数字指定的行数和列数打印该字符。
输入验证
在实际应用中,用户不一定会按照程序的指令行事。用户的输入和程序期望的输入不匹配时常发生,这会导致程序运行失败。作为程序员,除了完成编程的本职工作,还要事先预料一些可能的输入错误,这样才能编写出能检测并处理这些问题的程序。
例如,假设你编写了一个处理非负整数的循环,但是用户很可能输入一个负数。可以使用关系表达式来排除这种情况:
long n;
scanf("%ld", &n);//获取第一个值
while (n >= 0)//检测不在范围内的值
{
//处理n
scanf("%ld", &n); //获取下一个值
}
另一类潜在的陷阱是:用户可能输入错误类型的值,如字符q。排除这种情况的一种办法是,检查scanf()的返回值。scanf()返回成功读取项的个数。因此,下面的表达式当且仅当用户输入一个整数时才为真:
scanf("%ld", &n) == 1
结合上面的while循环,可改进为:
long n;
while (scanf("%ld", &n) == 1&& n >= 0)
{
//处理n
}
while循环条件可以描述为:“当输入是一个整数且该整数为非负整数时”。
对于最后的例子,当用户输入错误类型的值时,程序结束。然而,可以让程序友好些,提示用户再次输入正确类型的值。在这种情况下,要处理有问题的输入。如果scanf()没有成功获取,就会将其留在输入队列中。这里要明确,输入实际上是字符流。可以使用getchar()函数逐字符地读取输入,甚至可以把这些想法都结合在一个函数中:
/* input.c */
#include <stdio.h>
int main(void)
{
long input;
int num = 0;
puts("please input an integer greater than 0 or equal to 0");
while ((num = scanf("%ld", &input)) != 1 || input < 0)
{
if (num != 1 )
puts("the input value is not an integer");
else if (input < 0)
puts("the input value is less than 0");
while (getchar() != '\n')
continue;
puts("please input an integer greater than 0 or equal to 0");
}
printf("input:%ld\n", input);
return 0;
}
mali@mali:~/code/file$ ./input
please input an integer greater than 0 or equal to 0
a
the input value is not an integer
please input an integer greater than 0 or equal to 0
-9
the input value is less than 0
please input an integer greater than 0 or equal to 0
hello
the input value is not an integer
please input an integer greater than 0 or equal to 0
-10
the input value is less than 0
please input an integer greater than 0 or equal to 0
e
the input value is not an integer
please input an integer greater than 0 or equal to 0
10
input:10
mali@mali:~/code/file$