您好,欢迎来到小侦探旅游网。
搜索
您的当前位置:首页CC++下scanf的%匹配以及过滤字符串问题

CC++下scanf的%匹配以及过滤字符串问题

来源:小侦探旅游网
CC++下scanf的%匹配以及过滤字符串问题

最近在写⼀个测试的⼩程序,由于⽤到了sscanf函数对字符串进⾏标准读⼊,⽽sscanf在很多⽅⾯都与scanf⽐较相像,于是对scanf进⾏了⼀番测试,遇到了⼀系列基础性的问题,恶补基础的同时也体现了⾃⼰的薄弱。话不多说,直接附上问题吧。

问题描述

#include#include

int main(){

while(1) {

char str[256]={0}; int a=0; char b='\\0';

printf(\"please input the num:\\n\"); scanf(\"%[^,],%[^,],%d\

printf(\"input is str: %s b: %c a: %d And str length is %u, the first character is %u\\n\ //gets(str); }

return 0;}

在这段代码⾥,如果我初始输⼊\"a,a,0\",输出正确。但输⼊\的话,接下来会⾃动不需要输⼊然后循环着不断的输出,想问⼀下这个是什么原因。还有⼀个是我将代码改成了如下:

#include#include

int main(){

while(1) {

char str[256]={0}; int a=0; char b='\\0';

printf(\"please input the num:\\n\");

scanf(\"%*[\\n\\r\]%[^,],%[^,],%d\

printf(\"input is str: %s b: %c a: %d And str length is %u, the first character is %u\\n\ //gets(str); }

return 0;}

本来⽬的是过滤输⼊的回车,其中%*是过滤满⾜条件的字符,但是如果直接输⼊\"a,a,0\",也会出现不需要输⼊然后不断地循环输出的现象。

问题解答

scanf⾥⾯的任何%都⾄少要匹配1个字节,所以连续两个逗号是匹配不出任何东西的。注意这⾥,哪怕是%*...都是只要要匹配⼀个字符的,匹配不了的就是失败。⽽死循环的原因,是因为流⾥⾯⼀个字节都没有被接受,所以每次循环⾯对⼀摸⼀样的流。scanf每次都失败。⽰例:

1 #include 2 #include 3

4 int main() 5 {

6 while(1) 7 {

8 char str[256]={0}; 9 int a=-1;10 char b='\\0';

11 char test[256]={0};

12 printf(\"please input the num:\\n\");

13 int ret=scanf(\"%*[\\r\\n\]%[^,],%[^,],%d\",str,&b,&a);

14 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[0],ret);15 char testgets[256];16 gets(testgets);

17 printf(\"testgets:%s \\n\",testgets);18 }

19 return 0;20 }

在%号中若匹配不到任何字节,则scanf失败,标准输⼊缓冲区中的字段⼀直保留未被读取。 注:

%跟*则表⽰过滤掉满⾜条件的字符,%后可跟正则表达式。

此⽂所有的实验都是基于下⾯的程序:char str[10];

for (int i = 0; i < 10; i++) str[i] = '!';执⾏完后str的值为str = \"\"

我们把str的每个字符都初始化为惊叹号,当str的值发⽣变化时,使⽤printf打印str的值,对⽐先前的惊叹号,这样就可以⽅便的观察str发⽣了怎样的变化。下⾯我们做⼏个⼩实验,看看使⽤sscanf和正则表达式格式化输⼊后,str有什么变化。实验1:

sscanf( \"123456\" , \"%s\" , str) ; ---------str的值为 \"123456\\0\"

这个实验很简单,把源字符串\"123456\"拷贝到str的前6个字符,并且把str的第7个字符设为null字符,也就是\\0实验2:

sscanf( \"123456\" , \"%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个字符实验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

⾸先,%*[^0-9]过滤前⾯⾮数字的字符,然后⽤%i把数字字符转换成int型的整数,拷贝到变量k,注意参数必须使⽤k的地址。实验10:

sscanf(\"123334abcd123\", \"%[0-9]*\", str);

执⾏完后str的内容是字符串\"123334\"。[0-9]*是⼀个⾮常简单的正则表达式,意思是匹配数字任意次。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- xiaozhentang.com 版权所有 湘ICP备2023022495号-4

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务