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
}
运行结果

posted @ 2022-02-07 10:58  shucharjer  阅读(39)  评论(0)    收藏  举报