19. C Primer Plus 8.6输入验证
8.6输入验证
在生活中,用户不一定会按照程序的指令行事。用户的输入和程序期待的不输入不匹配时常发生,这将导致程序运行失败。我们在生活中除了应该完成编程的本职工作外,还要事先预料一些可能的输入错误,这样才能编写出能检测并处理这些问题的程序。
例如,我们可能编写了一个处理非负数整数的循环,但是用户很可能输入一个负数。这时我们可以使用关系表达式来排除这种情况:

另一类潜在的陷进是,用户可能输入错误类型的值,如字符q。排除这 种情况的一种方法是,检查scanf()的返回值。回忆一下,scanf()返回成功读取项的个数。因此下面的表达式当且仅当用户输入一个整数时才为真:

结合上面所使用的while循环,即可改进为:

while循环条件可以描述为“当输入是一个整数且该整数为正时”。
在上述示例中,当用户输入错误的类型的值时,程序结束。然而,也可以让程序友好些,提醒用户再次输入正确类型的值。在此情况下,要处理有问题的输入。如果scanf()没有成功读取,就会将其留在输入队列中。
注意:我们输入的实际上是字符流,可以使用getchar()函数逐字符地读取输入,可以把这些想法都结合在一个函数中,如下:


该函数要把一个int类型的值读入变量input中。如果读取失败,(1.)函数则进入外层while循环体。然后内层循环逐字符地读取错误的输入。注意,该函数丢弃该输入行的所有剩余内容。还有一种方法(2.)只丢弃一个字符或单词,然后该函数提示用户再次输入。外层循环重复运行,直到用户成功输入一个整数,此时scanf()的返回值为1.
在用户输入整数以后,程序可以检查该值的有效性。例如,要求用户输入一个上限和一个下限来定义值的范围。在程序中,我们可能希望程序检查第一个值是否大于第二个值(通常假设第一个值是较小的那个值),除此之外还有检查这些值是否在允许的范围内。例如查找档案时的年限范围,这个类似的限制可以在一个函数中实现。
假设程序中包含了stdbool.h 头文件。如果当前系统不允许使用_Bool, 把bool替换成int,把true 替换成 1,把 false 替换成 0 即可。注意,如果输入 无效,该函数返回 true,所以函数名为bad_limits():


下列示例程序演示了上面的两个函数为一个进行算数运算的函数提供整数,该函数计算特定范围内所有整数的平方和。程序限制范围上限为10000000,下限为-10000000:








输出结果如下:


8.6.1分析程序
程序遵循模块化的编程思想,使用独立函数(模块)来验证输入和管理 显示。程序越大,使用模块化编程就越重要。
main()函数管理程序流,为其他函数委派任务。它使用 get_long()获取 值、while 循环处理值、badlimits()函数检查值是否有效、sum_squres()函数 处理实际的计算:


8.6.2输入流和数字
在编写处理错误输入的代码时(如上述程序示例时),应该很清楚C是如何处理输入的。
比如:is 28 12.4
在我们眼中,这就像是一个由字符、整数和浮点数组成的字符串。但是,在C语言中,这就是一个字节流。第一个字节是字母i的字符编码,第二个字节是字母s的字符编码,第三个字节是空格字符的字符编码,以此类推。所以,当get_long()函数处理这一行输入时,第一个字符是非数字,那么整行输入都会被丢弃,包括其中的数字,由于这些数字也是该输入行的其他字符:

虽然输入流由字符组成,但是也可以设置scanf()函数把它们转换成数字。**例如,下例:
42
如果在scanf()函数中使用%c转换说明它只会读取字符4并将其储存在 char类型的变量中。如果使用%s转换说明,它会读取字符4和字符2这两个字 符,并将其储存在字符数组中。如果使用%d转换说明,scanf()同样会读取 两个字符,但是随后会计算出它们对应的整数值:4×10+2,即42,然后将 表示该整数的二进制数储存在 int 类型的变量中。如果使用%f 转换说明,scanf()也会读取两个字符,计算出它们对应的数值42.0,用内部的浮点表示法表示该值,并将结果储存在float类型的变量中。
总之,输入流由字符组成,但可以通过scanf()函数将输入转换成整数值或者浮点数值。
注意:使用转换说明(如%d或%f)限制了可接受输入的字符类型,而 getchar()和使用%c的scanf()接受所有的字符。

浙公网安备 33010602011771号