如果你想让自己的输入更加炫酷、更加个性化、更加安全,那么还需要学习 scanf() 的高级用法,这才是大神和菜鸟的分水岭。
好了,言归正传,我们分三个方面讲解 scanf() 的高级用法。
1) 指定读取长度
还记得在 printf() 中可以指定最小输出宽度吗?就是在格式控制符的中间加上一个数字,例如,d表示输出的整数至少占用 10 个字符的位置:
其实,scanf() 也有类似的用法,也可以在格式控制符的中间加一个数字,用来表示读取数据的最大长度,例如:
请看下面的例子:
#include <stdio.h> int main(){ int n; float f; char str[23]; scanf("%2d", &n); scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区 scanf("%5f", &f); scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区 scanf("%22s", str); printf("n=%d, f=%g, str=%s\n", n, f, str); return 0; }
输入示例 ①:
20↙ 100.5↙ http://c.biancheng.net↙ n=20, f=100.5, str=http://c.biancheng.net
输入示例 ②:
8920↙ 10.2579↙ http://data.biancheng.net↙ n=89, f=10.25, str=http://data.biancheng.
这段代码使用了多个 scanf() 函数连续读取数据,为了避免受到缓冲区中遗留数据的影响,每次读取结束我们都使用scanf("%*[^\n]"); scanf("%*c");来清空缓冲区。
限制读取数据的长度在实际开发中非常有用,最典型的一个例子就是读取字符串:我们为字符串分配的内存是有限的,用户输入的字符串过长就存放不了了,就会冲刷掉其它的数据,从而导致程序出错甚至崩溃;如果被黑客发现了这个漏洞,就可以构造栈溢出攻击,改变程序的执行流程,甚至执行自己的恶意代码,这对服务器来说简直是灭顶之灾。
在用 gets() 函数读取字符串的时候,有一些编译器会提示不安全,建议替换为 gets_s() 函数,就是因为 gets() 不能控制读取到的字符串的长度,风险极高。
2) 匹配特定的字符
%s 控制符会匹配除空白符以外的所有字符,它有两个缺点:
要想解决以上问题,可以使用 scanf() 的另外一种字符匹配方式,就是%[xxx],[ ]包围起来的是需要读取的字符集合。例如,%[abcd]表示只读取字符abcd,遇到其它的字符就读取结束;注意,这里并不强调字符的顺序,只要字符在 abcd 范围内都可以匹配成功,所以你可以输入 abcd、dcba、ccdc、bdcca 等。
请看下面的代码:
#include <stdio.h> int main(){ char str[30]; scanf("%[abcd]", str); printf("%s\n", str); return 0; }
输入示例 ①: