C

C 知识量:16 - 74 - 317

8.3 输入验证><

输入验证必要性- 8.3.1 -

在实际应用中,用户不一定按照程序的指令行事,用户的输入与程序期望值不匹配时常发生,如果没有应对,就会导致程序运行失败。因此,在编程时要提高程序的可靠性,就必须事先预料一些可能的输入错误,并编写出能够检测和处理这些问题的程序。

一个输入验证的例子- 8.3.2 -

下面借助一个较为复杂的示例来理解输入验证问题。示例程序的功能是计算特定范围内所有整数的平方和。限制数值的范围是-10000~10000。

#include <stdio.h>
//声明整数类型输入验证函数
long get_long(void);
//声明数值有效性验证函数
int bad_limits(long begin, long end, long low, long high);
//声明整数平方和计算函数
double sum_squares(long a, long b);

int main(void) {
    const long MIN = -10000L; //数值下限
    const long MAX = 10000L; //数值上限
    long start; //用户指定的范围最小值
    long stop; //用户指定的范围最大值
    double answer;
    printf("This program computes the sum of the squares "
            "of integer in a range(-10000~10000).\n"
            "Enter 0 for both limits to quit.\n");
    printf("Please input lower limit:");
    start = get_long();
    printf("Please input 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 "
                    "from %ld to %ld is %g\n", start, stop, answer);
        }
        printf("Enter 0 for both limits to quit.\n");
        printf("Please input lower limit:");
        start = get_long();
        printf("Please input upper limit:");
        stop = get_long();
    }
    printf("Done.\n");
    system("pause");
    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 input an integer value:");
    }
    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;
}

//数值有效性验证函数定义
int bad_limits(long begin, long end, long low, long high) {
    int not_good = 0;
    if (begin > end) {
        printf("%ld isn't smaller than %ld.\n", begin, end);
        not_good = 1;
    }
    if (begin < low || end < low) {
        printf("Values must be %ld or greater.\n", low);
        not_good = 1;
    }
    if (begin > high || end > high) {
        printf("Values must be %ld or less.\n", high);
        not_good = 1;
    }
    return not_good;
}

运行结果为:

This program computes the sum of the squares of integer in a range(-10000~10000).
Enter 0 for both limits to quit.
Please input lower limit:low
low is not an integer.
Please input an integer value:a big number
a big number is not an integer.
Please input an integer value:5
Please input upper limit:11
The sum of the squares of the integers from 5 to 11 is 476
Enter 0 for both limits to quit.
Please input lower limit:12
Please input upper limit:8
12 isn't smaller than 8.
Please try again.
Enter 0 for both limits to quit.
Please input lower limit:0
Please input upper limit:0
Done.

程序简析- 8.3.3 -

以上示例中,实际计算部分占比很小,而数据验证部分占比却很大,因为数据的正确输入是计算正确的基础和前提。

上面的示例体现了模块化编程思想,使用了独立的函数(模块)来验证输入和管理显示。程序越大越复杂,使用模块化编程就越重要。

  • 在以上代码中,main()函数是主函数,负责管理程序流,并为其他函数委派任务(调用其他函数)。

  • get_long()函数用于从用户获取值,并确保获得的是整数值。

  • bad_limits()函数用于检测值是否有效,验证值的范围和逻辑关系是否正确。

  • sum_squares()函数用于处理实际的计算,并向main()函数返回计算结果。

注意:在示例代码中,除了首先要导入头文件外,还要先声明要使用的自定义函数,即声明get_long()、bad_limits()和sum_squares()函数,后面虽然有它们的完整定义,但是程序自上而下编译,如果不事先声明,编译器就不认识它们,会报错。

输入流和数字- 8.3.4 -

在编写处理错误输入的代码时,应该明白C是如何处理输入的。例如:对于is 20 6.5,在我们眼中,这就是一个由字符、整数和浮点数组成的字符串。但对于C程序来说,这是一个字节流。第1个字节是字母i的字符编码,第2个字节是字母s的字符编码,第3个字节是空格字符的字符编码,第4个字节是数字2的字符编码,以此类推。因此,如果使用get_long()函数来处理这一行输入,因为第一个是非数字,所以整行输入都会被丢弃,包括其中的数字。

可以利用scanf()函数将输入流内容转换成数值。例如:对于45,如果使用%c转换说明,只会读取字符4,并储存到字符类型变量中;如果使用%s转换说明,会读取字符4和5,并存储为字符数组;如果使用%d转换说明,会读取字符4和5,并计算出对应的整数值45,存储到int类型变量中;如果使用%f转换说明,会读取字符4和5,并计算出对应的浮点值45.0,存储到float类型变量中。

总之,输入都由字符组成,C程序把输入作为传入的字节流,getchar()函数和scanf()函数把每个字符解释成一个字符编码。但是scanf()函数可以把输入转换成整数值或浮点数值,使用转换说明限制了可接受输入的字符类型。而getchar()函数和使用%c的scanf()函数可以接受所有的字符。