如果你想让自己的输入更加炫酷、更加个性化、更加安全,那么还需要学习 scanf() 的高级用法,这才是大神和菜鸟的分水岭。

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

1) 指定读取长度

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

  • 如果整数的宽度不足 10,那么在左边以空格补齐;
  • 如果整数的宽度超过了 10,那么以整数本身的宽度来输出,10 不再起作用。

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

  • %2d表示最多读取两位整数;
  • %10s表示读取的字符串的最大长度为 10,或者说,最多读取 10 个字符。

请看下面的例子:

#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 控制符会匹配除空白符以外的所有字符,它有两个缺点:

  1. %s 不能读取特定的字符,比如只想读取小写字母,或者十进制数字等,%s 就无能为力;
  2. %s 读取到的字符串中不能包含空白符,有些情况会比较尴尬,例如,无法将多个单词存放到一个字符串中,因为单词之间就是以空格为分隔的,%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;}

输入示例 ①:

abcdefgh↙
abcd

输入示例 ②:

baccbaxyz↙
baccba

使用连接符
为了简化字符集合的写法,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]表示读取所有的英文字母和十进制数字;
%[0-9a-f]表示读取十六进制数字。

请看下面的演示:

#include <stdio.h>
int main(){char str[30];
scanf("%[a-zA-Z]", str); //只读取字母
printf("%s\n", str);
return 0;
}

输入示例:

abcXYZ123↙
abcXYZ

不匹配某些字符
假如现在有一种需求,就是读取换行符以外的所有字符,或者读取 0~9 以外的所有字符,该怎么实现呢?总不能把剩下的字符都罗列出来吧,一是麻烦,二是不现实。

C语言的开发者们早就考虑到这个问题了,scanf() 允许我们在%[ ]中直接指定某些不能匹配的字符,具体方法就是在不匹配的字符前面加上^,例如:

%[^\n]表示匹配除换行符以外的所有字符,遇到换行符就停止读取;
%[^0-9]表示匹配除十进制数字以外的所有字符,遇到十进制数字就停止读取。

请看下面的例子:

#include <stdio.h>
int main(){char str1[30], str2[30];scanf("%[^0-9]", str1);scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区scanf("%[^\n]", str2);printf("str1=%s \nstr2=%s\n", str1, str2);return 0;
}

输入示例:

abcXYZ@#87edf↙
c c++ java python go javascript↙
str1=abcXYZ@#
str2=c c++ java python go javascript

请注意第 6 行代码,它的作用是读取一行字符串,和 gets() 的功能一模一样。你看,scanf() 也能读取带空格的字符串呀,谁说 scanf() 不能完全取代 gets(),这明显是错误的说法。

另外,scanf() 还可以指定字符串的最大长度,指定字符串中不能包含哪些字符,这是 gets() 不具备的功能。

例如,读取一行不能包含十进制数字的字符串,并且长度不能超过 30:

#include <stdio.h>
int main(){char str[31];
scanf("%30[^0-9\n]", str);
printf("str=%s\n", str);
return 0;
}

输入示例 ①:

http://c.biancheng.net http://biancheng.net↙
str=http://c.biancheng.net http://

输入示例 ②:

I have been programming for 8 years.↙
str=I have been programming for

3) 丢弃读取到的字符

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

%*d表示读取一个整数并丢弃;
%*[a-z]表示读取小写字母并丢弃;
%*[^\n]表示将换行符以外的字符全部丢弃。

请看下面的代码演示:

#include <stdio.h>
int main(){int n;
char str[30];
scanf("%*d %d", &n);
scanf("%*[a-z]");
scanf("%[^\n]", str);
printf("n=%d, str=%s\n", n, str);
return 0;
}

输入示例:

100 999abcxyzABCXYZ↙
n=999, str=ABCXYZ

对结果的分析:整数 100 被第一个 scanf() 中的%*d读取后丢弃了,整数 999 被第%d读取到,并赋值给 n。此时缓冲区中剩下 abcxyzABCXYZ,第二个 scanf() 将 abcxyz 读取并丢弃,剩下的 ABCXYZ 被最后一个 scanf() 读取到并赋值给 str。

大家有没有意识到,将读取到的字符直接丢弃,这就是在清空输入缓冲区呀,虽然有点蹩脚,但是行之有效。在C语言基础——清空缓存区 中我们已经给出了使用 scanf() 清空缓冲区的方案,就是:

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

下面我们就来解释一下。

首先需要明白的是,等到需要清空缓冲区的时候,缓冲区中的最后一个字符一定是换行符\n,因为输入缓冲区是行缓冲模式,用户按下回车键会产生换行符,结束本次输入,然后输入函数开始读取。

scanf("%*[^\n]");将换行符前面的所有字符清空,scanf("%*c");将最后剩下的换行符清空。

有些网友将这两条语句合并起来,写作:

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

这是错误的。合并以后的语句不能清空单个换行符,因为该语句要求换行符前边至少要有一个其它的字符,单个换行符会导致匹配失败。

总结

scanf() 控制字符串的完整写法为:

%{*} {width} type

其中,{ } 表示可有可无。各个部分的具体含义是:

type表示读取什么类型的数据,例如 %d、%s、%[a-z]、%[^\n] 等;type 必须有。
width表示最大读取宽度,可有可无。
*表示丢弃读取到的数据,可有可无。

C语言 —— 你不得不知道的 scanf 的高级用法相关推荐

  1. 20个你不得不知道的数组reduce高级用法

    目录 1 背景 2 高级用法 3 兼容和性能 4 结语 1 背景 reduce 作为ES5新增的常规数组方法之一,对比 forEach .filter 和 map,在实际使用上好像有些被忽略,发现身边 ...

  2. foreach用法_25个你不得不知道的数组reduce高级用法

    作者:JowayYoung 仓库:Github.CodePen 博客:掘金.思否.知乎.简书.头条.CSDN 公众号:IQ前端 联系我:关注公众号后有我的微信哟 特别声明:原创不易,未经授权不得对此文 ...

  3. es6中reduce的用法_25个你不得不知道的数组reduce高级用法

    背景 距离上一篇技术文章<1.5万字概括ES6全部特性>发布到现在,已经有整整4个月没有输出过一篇技术文章了.哈哈,不是不想写,而是实在太忙,这段时间每天不是上班就是加班,完全没有自己的时 ...

  4. java reduce.mdn_编程语言你不得不知道的数组reduce高级用法

    背景 距离上一篇技术文章<1.5万字概括ES6全部特性>发布到现在,已经有整整4个月没有输出过一篇技术文章了.哈哈,不是不想写,而是实在太忙,这段时间每天不是上班就是加班,完全没有自己的时 ...

  5. C语言你不得不知道的小知识

    C语言你不得不知道的小知识: 1: 常见的变量修饰关键字:static.const.extern.volatile.register static:(该变量保存在全局静态区)  a.修饰局部变量--& ...

  6. 【⛔C语言最熟悉的陌生人の你所不知道的scanf⛔】

    引子 CSDN最近搞了一个问答板块,我也没怎么关注,但是前几天收到一个广告,点进一看,回答首次被采纳,得现金红包!还有这种好事!想想我CSDN钱包里5块不到的余额,离100块的提现门槛的距离又近了一步 ...

  7. c语言node类型_高阶宏的妙用技法,C语言宏你所不知道的聪明技巧

    前言 什么是高阶宏?高阶宏就是以宏为参数的宏.以宏为参数的宏这就是这篇文章的重点.其实业界领先的代码库是有很多值得我们学习的,研究开源库也可以是大家学习的方向哦,通常开源库中90%的内容超出了新手对于 ...

  8. C语言:对从键盘输入的整数进行求和(scanf( )的高级用法)

    来自C Primer Plus(第五版)的第六章:循环(读书笔记) /* summing.c -- 对用户输入的整数求和 */ #include <stdio.h> int main(vi ...

  9. 同步器 java_您可能不知道的五个高级Java同步器

    同步器 java 除了基于每个Java对象具有的锁定位的通用同步外,您还可以使用Java中更复杂的同步器,例如: 信号量 –使用许可的概念表示一个位置中允许的最大线程数. 当使用值1时,其行为类似于同 ...

最新文章

  1. RESTful架构和RESTful API设计总结
  2. html 3d转换动画,开源项目:CSS 3D转换和动画学习示例教程
  3. 您的日志就是您的数据:logstash + elasticsearch
  4. 1.9 编程基础之顺序查找 01 查找特定的值
  5. java中加号和append,请详细说一下java中append()的方法.
  6. C和指针---读书笔记16
  7. 清华大学开源用于网络嵌入的工具包 OpenNE
  8. HDU 4289 Control
  9. 这些问题你是否也有中招?TMT行业质量报告新鲜出炉
  10. Android 清理大师产品的具体实现(一)QQ专清
  11. jQuery——滚动条位置的获取与设置
  12. Node 裁切图片的方法
  13. Day2:两招判断好生意--现金流三拷问
  14. C语言-qsort函数详解及使用例
  15. python实现天气查询
  16. SpO2、SaO2、PaO2、低氧血症概念及标准的总结
  17. RK3399 双路mipi拼接屏+HDMI 三屏显示
  18. 通过PS神还原“宫崎骏”式的动漫真人效果
  19. c语言迭代法求立方根,谁能解释一下立方根的迭代公式,要详细的,thankyou!
  20. python3爬虫简易入门图片_Python3爬虫图片抓取

热门文章

  1. List/Map 遍历
  2. 99%没用过的奇怪网站,黑科技应用网站推荐
  3. 四旋翼无人机建模与实现(一)
  4. QueryWrapper快速查一条数据
  5. PyQt5——pyqtgraph绘图大招
  6. 2019数学三考研真题线性代数部分解析
  7. 概率论:魏布斯分布Weibull cumulative distribution function
  8. Linux ARM 静态网络配置
  9. 事务(Transaction)的特性
  10. 文科生也能看懂,什么是熵