C/C++下scanf的%匹配以及过滤字符串问题
最近在写一个测试的小程序,由于用到了sscanf函数对字符串进行标准读入,而sscanf在很多方面都与scanf比较相像,于是对scanf进行了一番测试,遇到了一系列基础性的问题,恶补基础的同时也体现了自己的薄弱。
话不多说,直接附上问题吧。
问题描述
#include<stdio.h>
#include<string.h> int main()
{
while(1)
{
char str[256]={0};
int a=0;
char b='\0';
printf("please input the num:\n");
scanf("%[^,],%[^,],%d",str,&b,&a);
printf("input is str: %s b: %c a: %d And str length is %u, the first character is %u\n",str,b,a,strlen(str),str[0]);
//gets(str);
}
return 0;
}
在这段代码里,如果我初始输入"a,a,0",输出正确。但输入",,0"的话,接下来会自动不需要输入然后循环着不断的输出,想问一下这个是什么原因。还有一个是我将代码改成了如下:

#include<stdio.h>
#include<string.h> int main()
{
while(1)
{
char str[256]={0};
int a=0;
char b='\0';
printf("please input the num:\n");
scanf("%*[\n\r\t]%[^,],%[^,],%d",str,&b,&a);
printf("input is str: %s b: %c a: %d And str length is %u, the first character is %u\n",str,b,a,strlen(str),str[0]);
//gets(str);
}
return 0;
}

本来目的是过滤输入的回车,其中%*是过滤满足条件的字符,但是如果直接输入"a,a,0",也会出现不需要输入然后不断地循环输出的现象。
问题解答
scanf里面的任何%都至少要匹配1个字节,所以连续两个逗号是匹配不出任何东西的。注意这里,哪怕是%*...都是只要要匹配一个字符的,匹配不了的就是失败。而死循环的原因,是因为流里面一个字节都没有被接受,所以每次循环面对一摸一样的流。scanf每次都失败。
示例:
#include<stdio.h>
#include<string.h> int main()
{
while()
{
char str[]={};
int a=-;
char b='\0';
char test[]={};
printf("please input the num:\n");
int ret=scanf("%*[\r\n\t]%[^,],%[^,],%d",str,&b,&a);
printf("input is str: %s b: %c a: %d And str length is %u, the first character is %u.ret is %d \n",str,b,a,strlen(str),str[],ret);
char testgets[];
gets(testgets);
printf("testgets:%s \n",testgets);
}
return ;
}

在%号中若匹配不到任何字节,则scanf失败,标准输入缓冲区中的字段一直保留未被读取。
注:
%跟*则表示过滤掉满足条件的字符,%后可跟正则表达式。
此文所有的实验都是基于下面的程序:
char str[];
for (int i = ; i < ; i++) str[i] = '!';
执行完后str的值为
str = "!!!!!!!!!!"
我们把str的每个字符都初始化为惊叹号,当str的值发生变化时,使用printf打印str的值,对比先前的惊叹号,这样就可以方便的观察str发生了怎样的变化。下面我们做几个小实验,看看使用sscanf和正则表达式格式化输入后,str有什么变化。
实验1:
sscanf( "" , "%s" , str) ; ---------str的值为 "123456\0!!!"
这个实验很简单,把源字符串""拷贝到str的前6个字符,并且把str的第7个字符设为null字符,也就是\
实验2:
sscanf( "" , "%3s" , str) ; ---------str的值为 "123\0!!!!!!"
看到没有,正则表达式的百分号后面多了一个3,这告诉sscanf只拷贝3个字符给str,然后把第4个字符设为null字符。
实验3:
sscanf( "aaaAAA" , "%[a-z]" , str) ; ---------str的值为 "aaa\0!!!!!!"
从这个实验开始我们会使用正则表达式,括号里面的a-z就是一个正则表达式,它可以表示从a到z的任意字符,
在继续讨论之前,我们先来看看百分号表示什么意思,%表示选择 ,%后面的是条件,比如实验1的"%s",s是一个条件,表示任意字符,"%s"的意思是:只要输入的东西是一个字符,就把它拷贝给str。实验2的"%3s"又多了一个条件:只拷贝3个字符。实验3的“%[a-z]”的条件稍微严格一些,输入的东西不但是字符,还得是一个小写字母的字符,所以实验3只拷贝了小写字母"aaa"给str,别忘了加上null字符。
实验4:
sscanf( "AAAaaaBBB" , "%[^a-z]" , str) ; ---------str的值为 "AAA\0!!!!!!"
对于所有字符,只要不是小写字母,都满足"^a-z"正则表达式,符号^表示逻辑非。前3个字符都不是小写字符,所以将其拷贝给str,但最后3个字符也不是小写字母,为什么不拷贝给str呢?这是因为当碰到不满足条件的字符后,sscanf就会停止执行,不再扫描之后的字符。
实验5:
sscanf( "AAAaaaBBB" , "%[A-Z]%[a-z]" , str) ; ---------段错误
这个实验的本意是:先把大写字母拷贝给str,然后把小写字母拷贝给str,但很不幸,程序运行的时候会发生段错误,因为当sscanf扫描到字符a时,违反了条件"%[A-Z]",sscanf就停止执行,不再扫描之后的字符,所以第二个条件也就没有任何意义,这个实验说明:不能使用%号两次或两次以上
实验6: sscanf( "AAAaaaBBB" , "%*[A-Z]%[a-z]" , str) ; ---------str的值为 "aaa\0!!!!!!"
这个实验出现了一个新的符号:%*,与%相反,%*表示过滤 满足条件的字符,在这个实验中,%*[A-Z]过滤了所有大写字母,然后再使用%[a-z]把之后的小写字母拷贝给str。如果只有%*,没有%的话,sscanf不会拷贝任何字符到str,这时sscanf的作用仅仅是过滤字符串。
实验7:
sscanf( "AAAaaaBBB" , "%[a-z]" , str) ; ---------str的值为 "!!!!!!!!!!"
做完前面几个实验后,我们都知道sscanf拷贝完成后,还会在str的后面加上一个null字符,但如果没有一个字符满足条件,sscanf不会在str的后面加null字符,str的值依然是10个惊叹号。这个实验也说明了,如果不使用%*过滤掉前面不需要的字符,你永远别想取得中间的字符。
实验8:
sscanf( "AAAaaaBC=" , "%*[A-Z]%*[a-z]%[^a-z=]" , str) ; ---------str的值为 "BC\0!!!!!!!"
这是一个综合实验,但这个实验的目的不是帮我们复习前面所学的知识,而是展示两个值得注意的地方:
注意1:%只能使用一次,但%*可以使用多次,比如在这个实验里面,先用%*[A-Z]过滤大写字母,然后用%*[a-z]过滤小写字母。
注意2:^后面可以带多个条件,且这些条件都受^的作用,比如^a-z=表示^a-z且^=(既不是小写字母,也不是等于号)。
实验9:
int k;
sscanf( "AAA123BBB456" , "%*[^0-9]%i" , &k) ; ---------k的值为123
首先,%*[^-]过滤前面非数字的字符,然后用%i把数字字符转换成int型的整数,拷贝到变量k,注意参数必须使用k的地址。
实验10:
sscanf("123334abcd123", "%[0-9]*", str);
执行完后str的内容是字符串""。[-]*是一个非常简单的正则表达式,意思是匹配数字任意次。
C/C++下scanf的%匹配以及过滤字符串问题的更多相关文章
- linux替换目录下所有文件中的某字符串
linux替换目录下所有文件中的某字符串 比如,要将目录/modules下面所有文件中的zhangsan都修改成lisi,这样做: sed -i "s/zhangsan/lisi/g&quo ...
- js进阶正则表达式实现过滤字符串(RegExp对象操作正则表达式)(正则:regular)(表达式:expression)
js进阶正则表达式实现过滤字符串(RegExp对象操作正则表达式)(正则:regular)(表达式:expression) 一.总结 1.str_replace:正则作用:高效快速匹配 2.break ...
- 去空格 whitespaceAndNewlineCharacterSet和过滤字符串
一.过滤字符串 可以使用stringByTrimmingCharactersInSet函数过滤字符串中的特殊符号 首先自己定义一个NSCharacterSet, 包含需要去除的特殊符号 NSChara ...
- c# 过滤字符串中的重复字符
有字符串"a,s,d,v,a,v",如果想去除其中重复的字符,怎么做? 下面是一个方法,用Hashtable来记录唯一字符,排除重复字符,仅供参考. 1.过滤方法: public ...
- mysql下sql语句 update 字段=字段+字符串
mysql下sql语句 update 字段=字段+字符串 mysql下sql语句令某字段值等于原值加上一个字符串 update 表明 SET 字段= 'feifei' || 字段; (postgr ...
- javascript 过滤字符串中的中文与空格
js 如何过滤字符串里中文或空格呢?方法有很多种,我们可以使用替换与正则表达式来实现,本文向大家介绍两个简单的例子,感兴趣的码农可以参考一下. 1.javascript过滤空格: function m ...
- 《Python CookBook2》 第一章 文本 - 过滤字符串中不属于指定集合的字符 && 检查一个字符串是文本还是二进制
过滤字符串中不属于指定集合的字符 任务: 给定一个需要保留的字符串的集合,构建一个过滤函数,并可将其应用于任何字符串s,函数返回一个s的拷贝,该拷贝只包含指定字符集合中的元素. 解决方案: impor ...
- 过滤字符串html标签方法
过滤字符串html标签方法,如果输入的过滤标签为“*”,那么给字符串加上p标签 public static string noTagHtml(string str, string tagname) { ...
- 1.3 正则表达式和python语言-1.3.6匹配多个字符串
1.3.6 匹配多个字符串(2018-05-08) 我们在正则表达式 bat|bet|bit 中使用了择一匹配(|)符号.如下为在 Python中使用正则表达式的方法. import re #bat| ...
随机推荐
- 【PAT】1011. A+B和C (15)
1011. A+B和C (15) 给定区间[-231, 231]内的3个整数A.B和C,请判断A+B是否大于C. 输入格式: 输入第1行给出正整数T(<=10),是测试用例的个数.随后给出T组测 ...
- 【LOJ】#2350. 「JOI 2017/2018 决赛」月票购买
题解 首先求一个最短路图出来,最短路图就是这条边在最短路上就保留,否则就不保留,注意最短路图是一个有向图,一条边被保留的条件是 dis(S,u) + val(u,v) = dis(v,T)我们需要求两 ...
- Join 与 CountDownLatch 之间的区别
Join 与 CountDownLatch 之间的区别 import java.util.concurrent.CountDownLatch; public class CountDownLatchT ...
- 500 多个 Linux 命令文档搜索
500 多个 Linux 命令文档搜索 搜索界面:https://wangchujiang.com/linux-command/ 源码:https://github.com/jaywcjlove/li ...
- JQuery经典总结
1.jQuery介绍 jQuery是一个js框架(其实就是一个.js文件),它的特点是使用选择器查找要操作的节点,并且将这些节点封装成一个jQuery对象.封装的目的是为了更好地兼容不同的浏览器之间的 ...
- thinkphp5.0动态配置
设置配置参数 使用set方法动态设置参数,例如: Config::set('配置参数','配置值'); // 或者使用助手函数 config('配置参数','配置值'); 也可以批量设置,例如: Co ...
- mysql左连接,右连接,内连接
- 洛谷P1552 [APIO2012] 派遣 [左偏树,树形DP]
题目传送门 忍者 Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都 ...
- 【BZOJ 3160】 3160: 万径人踪灭 (FFT)
3160: 万径人踪灭 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1440 Solved: 799 Description Input Outp ...
- 「WC2016」论战捆竹竿
「WC2016」论战捆竹竿 前置知识 参考资料:<论战捆竹竿解题报告-王鉴浩>,<字符串算法选讲-金策>. Border&Period 若前缀 \(pre(s,x)\ ...