首页
学习
活动
专区
工具
TVP
发布

C语言学习:scanf的高级用法

在上一篇中,我们演示了如何使用 scanf() 来读取各种各样的数据,汇总了 scanf() 可以使用的格式控制符,从根本上消除了 scanf() 的那些奇怪行为,至此,很多初学者就认为自己已经完全掌握了 scanf()。不太明白的可以先看我的上一篇。

其实,这只是 scanf() 的基本用法,每个C语言程序员都应该掌握,如果你想让自己的输入更加炫酷、更加个性化、更加安全,那么还需要学习 scanf() 的高级用法,这才是大神和菜鸟的分水岭。

想学习C/C++加群753168139

好了,言归正传,我们分三个方面讲解 scanf() 的高级用法。

1) 指定读取长度

还记得在 printf() 中可以指定最小输出宽度吗?就是在格式控制符的中间加上一个数字,例如,%10d

表示输出的整数至少占用 10 个字符的位置:

如果整数的宽度不足 10,那么在左边以空格补齐;

如果整数的宽度超过了 10,那么以整数本身的宽度来输出,10 不再起作用。

其实,scanf() 也有类似的用法,也可以在格式控制符的中间加一个数字,用来表示读取数据的最大长度,例如:

%2d 表示最多读取两位整数;

%10s 表示读取的字符串的最大长度为 10,或者说,最多读取 10 个字符。

#include

int main()

{

int n;

float f;

char str[30];

scanf("%2d", &n);

scanf("%*[^ ]"); scanf("%*c");

/*清空缓冲区,这是scanf函数的厉害之处,特定格式,直接记住,

关于缓冲区比较复杂后面我会写一个比较详细的总结*/

scanf("%5f", &f);

scanf("%*[^ ]"); scanf("%*c"); //清空缓冲区

scanf("%20s", str);//注意在scanf中,获取变量是需要*,字符串不需要*。

printf("n=%d, f=%g, str=%s ", n, f, str);

return 0;

}

运行结果:

想学习C/C++加群753168139

这段代码使用了多个 scanf() 函数连续读取数据,为了避免受到缓冲区中遗留数据的影响,每次读取结束我们都使用

scanf("%*[^ ]"); scanf("%*c");来清空缓冲区。

第一个scanf两位长度,我们输入了10.第二个scanf长度为5,我们输入了许多666,结果只接受了5个6.

关于缓冲区,没有清空缓冲区,我们无法连续多次输入scanf的值,清空缓冲区可以的简单理解为清除上一个scanf的值,这样就不会程序报错。

限制读取数据的长度在实际开发中非常有用,最典型的一个例子就是读取字符串:我们为字符串分配的内存是有限的,用户输入的字符串过长就存放不了了,就会冲刷掉其它的数据,从而导致程序出错甚至崩溃;如果被黑客发现了这个漏洞,就可以构造栈溢出攻击,改变程序的执行流程,甚至执行自己的恶意代码,这对服务器来说简直是灭顶之灾。

虽然 scanf() 可以控制字符串的长度,但是字符串中却不能包含空白符,这是硬伤,所以 scanf() 暂时还无法替代 gets()。不过大家也不要着急,稍后我还会补充 scanf() 的高级用法,届时 scanf() 就可以完全替代 gets(),并且比 gets() 更加智能

2) 匹配特定的字符

%s 控制符会匹配除空白符以外的所有字符,它有两个缺点:

%s 不能读取特定的字符,比如只想读取小写字母,或者十进制数字等,%s 就无能为力;

%s 读取到的字符串中不能包含空白符,有些情况会比较尴尬,例如,无法将多个单词存放到一个字符串中,因为单词之间就是以空格为分隔的,%s 遇到空格就读取结束了。

要想解决以上问题,可以使用 scanf() 的另外一种字符匹配方式,就是%[xxx]

[ ]包围起来的是需要读取的字符集合。例如,%[abcd]表示只读取字符abcd遇到其它的字符就读取结束;注意,这里并不强调字符的顺序,只要字符在 abcd 范围内都可以匹配成功,所以你可以输入 abcd、dcba、ccdc、bdcca 等。

请看下面的代码:

#include

int main(){

char str[30];

scanf("%[abcd]", str);

printf("%s ", str);

return 0;

}

运行结果:

想学习C/C++加群753168139

只读取abcd

使用连接符

为了简化字符集合的写法,scanf() 支持使用连字符"-"来表示一个范围内的字符,例如 %[a-z]、%[0-9] 等。

连字符左边的字符对应一个 ASCII 码,连字符右边的字符也对应一个 ASCII 码,位于这两个 ASCII 码范围以内的字符就是要读取的字符。注意,连字符左边的 ASCII 码要小于右边的,如果反过来,那么它的行为是未定义的。

常用的连字符举例:

%[a-z] 表示读取 abc...xyz 范围内的字符,也即小写字母;

%[A-Z] 表示读取 ABC...XYZ 范围内的字符,也即大写字母;

%[0-9] 表示读取 012...789 范围内的字符,也即十进制数字。

%[a-zA-Z] 表示读取大写字母和小写字母,也即所有英文字母。

%[a-z-A-Z0-9] 表示读取所有的英文字母和十进制数字。

这里的例子就参照上面的得到并输出abcd的例子改一改,

scanf("%[a-zA-Z]", str); //只读取字母(参考)

3) 丢弃读取到的字符

在前面的代码中,每个格式控制符都要对应一个变量,把读取到的数据放入对应的变量中。其实你也可以不这样做,scanf() 允许把读取到的数据直接丢弃,不往变量中存放,具体方法就是在 % 后面加一个*,例如:

%*d 表示读取一个整数并丢弃;

%*[a-z] 表示读取小写字母并丢弃;

%*[^ ] 表示将换行符以外的字符全部丢弃。

举例:

#include

int main(){

int n;

char str[30];

scanf("%*d %d", &n);

scanf("%*[a-z]");

scanf("%[^ ]", str);

printf("n=%d, str=%s ", n, str);

return 0;

}

运行结果:

想学习C/C++加群753168139

仔细分析结果

对结果的分析:整数 100 被第一个 scanf() 中的%*d读取后丢弃了,整数 999 被第%d读取到,并赋值给 n。

此时缓冲区中剩下 abcxyzABCXYZ,第二个 scanf() 将 abcxyz 读取并丢弃,剩下的 ABCXYZ 被最后一个 scanf() 读取到并赋值给 str。

大家有没有意识到,将读取到的字符直接丢弃,这就是在清空输入缓冲区呀

首先需要明白的是,等到需要清空缓冲区的时候,缓冲区中的最后一个字符一定是换行符 ,

因为输入缓冲区是行缓冲模式,用户按下回车键会产生换行符,结束本次输入,然后输入函数开始读取。

scanf("%*[^ ]");将换行符前面的所有字符清空,

scanf("%*c");将最后剩下的换行符清空。‍

这就是scanf的高级用法了,如果你全部理解了,scanf你就是大神了。不明白的收藏起来慢慢看,

以后的某天你敲代码的时候或许就明白了,还有scanf的基本用法在上一篇写了,有不明白可以看我的上一篇,

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180424A0FRA900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券