1月21日 C Primer Plus学习
8.6 输入验证
在实际应用中,用户不一定会按照程序的指令行事。用户的输入和程序期望的输入不匹配时常发生,这会导致程序运行失败。作为程序员,除了完成编程的本职工作,还要事先预料一些可能的输入错误,这样才能编写出能检测并处理这些问题的程序。
// checking.c -- 输入验证
#include <stdio.h>
#include <stdbool.h>
// 验证输入是一个整数
long get_long(void);
// 验证范围的上下限是否有效
bool bad_limits(long begin, long end,
long low, long high);
// 计算a~b之间的整数平方和
double sum_squares(long a, long b);
int main(void)
{
const long MIN = -10000000L; // 范围的下限 const long MAX = +10000000L; // 范围的上限
long start; // 用户指定的范围最小值 long stop; // 用户指定的范围最大值
double answer; printf("This program computes the sum of the squares of "
"integers in a range.\nThe lower bound should not "
"be less than -10000000 and\nthe upper bound "
"should not be more than +10000000.\nEnter the "
"limits (enter 0 for both limits to quit):\n"
"lower limit: ");
start = get_long();
printf("upper limit: ");
stop = get_long();
while (start != 0 || stop != 0)
{
if (bad_limits(start, stop, MIN, MAX))
printf("Please try again.\n");
else
{
answer = sum_squares(start, stop);
printf("The sum of the squares of the integers ");
printf("from %ld to %ld is %g\n", start, stop, answer);
}
printf("Enter the limits (enter 0 for both "
"limits to quit):\n");
printf("lower limit: ");
start = get_long();
printf("upper limit: ");
stop = get_long();
}
printf("Done.\n");
return 0;
}
long get_long(void)
{
long input; char ch;
while (scanf("%ld", &input) != 1)
{
while ((ch = getchar()) != '\n')
putchar(ch); // 处理错误输入
printf(" is not an integer.\nPlease enter an ");
printf("integer value, such as 25, -178, or 3: ");
}
return input;
}
double sum_squares(long a, long b)
{
double total = 0;
long i;
for (i = a; i <= b; i++)
total += (double) i * (double) i;
return total;
}
bool bad_limits(long begin, long end, long low, long high)
{
bool not_good = false;
if (begin > end)
{
printf("%ld isn't smaller than %ld.\n", begin, end);
not_good = true;
}
if (begin < low || end < low)
{
printf("Values must be %ld or greater.\n", low);
not_good = true;
}
if (begin > high || end > high)
{
printf("Values must be %ld or less.\n", high);
not_good = true;
}
return not_good;
}
8.6.1 分析程序
程序遵循模块化的编程思想,使用独立函数(模块)来验证输入和管理显示。程序越大,使用模块化编程就越重要。 main()函数管理程序流,为其他函数委派任务。
它使用 get_long()获取值、while 循环处理值、badlimits()函数检查值是否有效、sum_squres()函数处理实际的计算。
8.6.2 输入流和数字
简而言之,输入由字符组成,但是scanf()可以把输入转换成整数值或浮点数值。使用转换说明(如%d或%f)限制了可接受输入的字符类型,而 getchar()和使用%c的scanf()接受所有的字符。
8.7 菜单浏览
菜单给用户提供了一份响应程序的选项。假设有下面一个例子:
Enter the letter of your choice:
a. advice b. bell
c. count q. quit
理想状态是,用户输入程序所列选项之一,然后程序根据用户所选项完成任务。作为一名程序员,自然希望这一过程能顺利进行。因此,第1个目标是:当用户遵循指令时程序顺利运行;第2个目标是:当用户没有遵循指令时,程序也能顺利运行。显而易见,要实现第 2 个目标难度较大,因为很难预料用户在使用程序时的所有错误情况。
现在的应用程序通常使用图形界面,可以点击按钮、查看对话框、触摸图标,而不是我们示例中的命令行模式。但是,两者的处理过程大致相同:给用户提供选项、检查并执行用户的响应、保护程序不受误操作的影响。除了界面不同,它们底层的程序结构也几乎相同。但是,使用图形界面更容易通过限制选项控制输入。
8.7.1 任务
一个菜单程序需要获取用户的响应,根据响应选择要执行的动作。另外,程序应该提供返回菜单的选项。
C 的 switch 语句是根据选项决定行为的好工具,用户的每个选择都可以对应一个特定的case标签。
使用while语句可以实现重复访问菜单的功能。
8.7.2 使执行更顺利
缓冲输入依旧带来些麻烦,程序把用户每次按下 Return 键产生的换行符视为错误响应。为了让程序的界面更流畅,该函数应该跳过这些换行符。这类问题有多种解决方案。一种是用名为get_first()的新函数替换getchar()函数,读取一行的第1个字符并丢弃剩余的字符。这种方法的优点是,把类似act这样的输入视为简单的a,而不是继续把act中的c作为选项c的一个有效的响应。
8.7.3 混合字符和数值输入
void count(void)
{
int n, i;
printf("Count how far? Enter an integer:\n");
scanf("%d", &n);
for (i = 1; i <= n; i++)
printf("%d\n", i);
}
如果输入3作为响应,scanf()会读取3并把换行符留在输入队列中。下次
调用 get_choice()将导致get_first()返回这个换行符,从而导致我们不希望出现的行为。
重写 get_first(),使其返回下一个非空白字符而不仅仅是下一个字符,即可修复这个问题。我们把这个任务留给读者作为练习。另一种方法是,在 count()函数中清理换行符,如下所示:
void count(void)
{
int n, i;
printf("Count how far? Enter an integer:\n");
n = get_int();
for (i = 1; i <= n; i++)
printf("%d\n", i);
while (getchar() != '\n')
continue;
}
void count(void)
{ int n, i; printf("Count how far? Enter an integer:\n"); n = get_int(); for (i = 1; i <= n; i++)
printf("%d\n", i); while (getchar() != '\n') continue;
}
要注意在处理较复杂的任务时,如何让函数把任务委派给另一个函数。这样让程序更模块化。
编程练习第8题
代码
点击查看
#include<stdio.h>
float getNum(void);//获取输入要计算的数的函数
void getch(void);
int main(void)
{
char calculateType;
float num1,num2;
do
{
printf("选择你要进行的运算:\n"
"a. 加 b. 减\n"
"c. 乘 d. 除\n"
"q. 退出\n");
scanf("%c",&calculateType);
switch(calculateType)
{
case 'a':
num1 = getNum();
num2 = getNum();
printf("运算结果为:%f\n",num1+num2);
getch();//在这里调用这个函数是为了让结果出现在底端,有一个更好的观感
break;
case 'b':
num1 = getNum();
num2 = getNum();
printf("运算结果为:%f\n",num1-num2);
getch();
break;
case 'c':
num1 = getNum();
num2 = getNum();
printf("运算结果为:%f\n",num1*num2);
getch();
break;
case 'd':
num1 = getNum();
num2 = getNum();
printf("运算结果为:%f\n",num1/num2);
getch();
//接着依旧会运行下面的break
case 'q':
break;
default:
getch();
printf("请按照提示输入内容!\n");
}
}
while(calculateType != 'q');
fflush(stdin);//避免回车键带来的问题
return 0;
}
float getNum(void)
{
printf("请输入一个数:\n");
float num;
if(scanf("%f",&num) == 1)
{
fflush(stdin);
return num;
}
else//处理不按要求输入的内容
{
fflush(stdin);//避免回车键带来的问题,否则会成永动机?但是试了一下,最终会停下,应该是有递归的次数限制啥的
printf("请按照提示输入内容!\n");
getNum(); //递归
}
}
void getch(void)
{
char ch;
do
{
scanf("%c",&ch);
}
while(ch != 10);//回车键的键值是10
}
运行结果




浙公网安备 33010602011771号