[C/C++]scanf与sscanf

[C/C++]scanf与sscanf

一、scanf基本概念

scanf函数是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中,其关键字最末一个字母f即为“格式”(format)之意。

scanf函数的一般形式

scanf(格式控制,地址表列)

int scanf(char *format[,argument,...]);

“格式控制”的含义同printf函数;“地址表列”是由若干个地址组成的表列,可以是变量的地址,或字符串的首地址。

scanf()函数返回成功赋值的数据项数,读到文件末尾出错时则返回EOF。

注意上面的scanf("%d,%d,%d",&a,&b,&c);中%d,%d,%d之间有逗号,在输入数据时也要加逗号,如果去掉逗号,输入时就不用逗号,而用空格,tab键或回车键将各个数据隔开


格式字符说明

%a,%A 读入一个浮点值(仅C99有效)

%c 读入一个字符

%i 读入十进制,八进制,十六进制整数

%o 读入八进制整数

%x,%X 读入十六进制整数

%s 读入一个字符串,遇空格、制表符或换行符结束。

%f,%F,%e,%E,%g,%G 用来输入实数,可以用小数形式或指数形式输入。

%p 读入一个指针

%u 读入一个无符号十进制整数

%n 至此已读入值的等价字符数

%[] 扫描字符集合

%% 读%符号

附加格式说明字符表修饰符说明

L/l 长度修饰符 输入"长"数据

h 长度修饰符 输入"短"数据

W 整型常数 指定输入数据所占宽度

* 表示本输入项在读入后不赋值给相应的变量

 

 

scanf的返回值

scanf的返回值由后面的参数决定

scanf("%d%d", &a, &b);

如果a和b都被成功读入,那么scanf的返回值就是2

如果只有a被成功读入,返回值为1

如果a和b都未被成功读入,返回值为0

如果遇到错误或遇到end of file,返回值为EOF。

且返回值为int型.

 

 

 

使用scanf函数时应该注意的问题

1、scanf()中的变量必须使用地址。

2、scanf()的格式控制串可以使用其它非空白字符,但在输入时必须输入这些字符。

3、在用"%c"输入时,空格和“转义字符”均作为有效字符。

问题一

scanf()函数不能正确接受有空格的字符串?如: I love you!

  1. int main()    
  2. {    
  3.     char str[80];    
  4.     scanf("%s",str);    
  5.     printf("%s",str);    
  6.     return 0;    
  7. }    

输入:I love you!

输出:scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数,scanf函数仅在每一个数据域均有数据,并按回车后结束)。

① 遇空格、“回车”、“跳格”键。

② 遇宽度结束。

③ 遇非法输入。

所以,上述程序并不能达到预期目的,scanf()扫描到"I"后面的空格就认为对str的赋值结束,并忽略后面的"love you!".这里要注意是"love you!"还在键盘缓冲区(关于这个问题,网上我所见的说法都是如此,但是,我经过调试发现,其实这时缓冲区字符串首尾指针已经相等了,也就是说缓冲区清空了,scanf()函数应该只是扫描stdin流,这个残存信息是在stdin中)。我们改动一下上面的程序来验证一下:

  1. #include<windows.h>  
  2. int main()  
  3.   
  4. {  
  5.   
  6.     char str[80];  
  7.     char str1[80];  
  8.     char str2[80];  
  9.     scanf("%s",str);  
  10.     printf("%s",str);  
  11.     Sleep(5000);  
  12.     scanf("%s",str2);  
  13.     printf("\n%s",str1);  
  14.     printf("\n%s",str2);  
  15.     return 0;  
  16. }  

输入:I love you!

1

输出:

I

love

you!

好了,原因知道了,那么scanf()函数能不能完成这个任务?回答是:能!别忘了scanf()函数还有一个 %[] 格式控制符,请看下面的程序:

  1. int main()  
  2. {  
  3.     char string[50];  
  4.     scanf("%[^\n]",string);  
  5.     printf("%s\n",string);  
  6.     return 0;  
  7. }  

问题二

键盘缓冲区残余信息问题

  1. int main()  
  2. {  
  3.     int a;  
  4.     char c; do  
  5.     {  
  6.         scanf("%d",&a);  
  7.         scanf("%c",&c);  
  8.         printf("a=%d c=%c\n",a,c);  
  9.   
  10.     }while(c!='N');  
  11. }  

scanf("%c",&c);这句不能正常接收字符,什么原因呢?我们用printf("c=%d\n",c);将C用int表示出来,启用printf("c=%d\n",c);这一句,看看scanf()函数赋给C到底是什么,结果是c=10 ,ASCII值为10是什么?换行即\n.对了,我们每击打一下"Enter"键,向键盘缓冲区发去一个“回车”(\r),一个“换行"(\n),在这里 \r被scanf()函数处理掉了(姑且这么认为吧^_^),而\n被scanf()函数“错误”地赋给了c.解决办法:可以在两个scanf()函数之后加个fflush(stdin);,还有加getch() , getchar()也可以,但是要视具体scanf()语句加那个,这里就不分析了,读者自己去摸索吧。但是加fflush(stdin);不管什么情况都可行。

函数名: fflush

功 能: 清除一个流

用 法: int fflush(FILE *stream);

  1. #include <stdio.h>  
  2. int main()  
  3. {  
  4.     int a;  
  5.     char c; do  
  6.     {  
  7.         scanf("%d",&a);  
  8.         fflush(stdin);  
  9.         scanf("%c",&c);  
  10.         fflush(stdin);  
  11.         printf("a=%d c=%c\n",a,c); }while(c!='N');  

这里再给一个用“空格符”来处理缓冲区残余信息的示例:运行出错的程序:

  1. int main()
  2. {  
  3.     int i;  
  4.     char j;  
  5.     for(i = 0;i < 10;i++)  
  6.         scanf("%c",&j);  
  7. }  

使用了空格控制符后:

  1. int main()
  2. {  
  3.     int i;  
  4.     char j;  
  5.     for(i = 0;i < 10;i++)  
  6.         scanf(" %c",&j);  
  7. }  

可以运行看看两个程序有什么不同。

问题三

如何处理scanf()函数误输入造成程序死锁或出错?

  1. #include <stdio.h>  
  2. int main()  
  3. {  
  4.     int a,b;  
  5.     scanf("%d,%d",&a,&b);    
  6.     printf("%d+%d=%d",a,b,a+b);  
  7. }  

如上程序,如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个错误的结果,呵呵,这可能所有人都遇到过的问题吧?解决方法:scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个 scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题。正确的例程:

  1. int main()  
  2. {  
  3.     int a,b,c;  
  4.     while(scanf("%d,%d",&a,&b)!=2)fflush(stdin);  
  5.     printf("%d+%d=%d",a,b,a+b);  
  6. }  

补充

fflush(stdin)这个方法在GCC下不可用。(在VC6.0下可以)

以下是 C99 对 fflush 函数的定义:

int fflush(FILE *stream);

如果stream指向输出流或者更新流(update stream),并且这个更新流最近执行的操作不是输入,那么fflush函数将把任何未被写入的数据写入stream指向的文件(如标准输出文件stdout)。否则,fflush函数的行为是不确定的。

fflush(NULL)清空所有输出流和上面提到的更新流。如果发生写错误,fflush函数会给那些流打上错误标记,并且返回EOF,否则返回0。

由此可知,如果 stream 指向输入流(如 stdin),那么 fflush 函数的行为是不确定的。故而使用fflush(stdin) 是不正确的,至少是移植性不好的。

可采用如下方法:

  1. void flush()  
  2. {  
  3.     char c;  
  4.     while ((c=getchar()) != '\n'&&c!=EOF) ;  
  5. }  
  6. #include <stdio.h>  
  7. int main()  
  8. {  
  9.     int a,b,c;  
  10.     while(scanf("%d,%d",&a,&b)!=2) flush();  
  11.     c=a+b;  
  12.     printf("%d+%d=%d",a,b,c);  
  13. }

二,sscanf相关

看了几篇介绍sscanf函数,真是发现自己好多东西没理解透。

第一篇:

此文所有的实验都是基于下面的程序:

  1. char str[10];  
  2. for (int i = 0; i < 10; i++) 
  3. str[i] = '!';  

执行完后str的值为

  1. str = "!!!!!!!!!!"  

下面我们做几个小实验,看看使用sscanf和正则表达式格式化输入后,str有什么变化。


实验1:

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

注意:原字符串只有前七个被覆盖!同时将原字符的的第7个字符‘\0’也同时复制了过来!


实验2:

  1. sscanf( "123456" , "%3s" , str) ; ---------str的值为 "123\0!!!!!!"

此时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:

  1. sscanf( "AAAaaaBBB" , "%*[A-Z]%[a-z]" , str) ; ---------str的值为 "aaa\0!!!!!!"

这个实验出现了一个新的符号:%*,与%相反,%*表示过滤满足条件的字符,在这个实验中,%*[A-Z]过滤了所有大写字母,然后再使用%[a-z]把之后的小写字母拷贝给str。

实验7:


sscanf( "AAAaaaBBB" , "%[a-z]" , str) ; ---------str的值为 "!!!!!!!!!!"

如果sscanf函数未找到符合条件的字符传入,则str将保持原值!做完前面几个实验后,我们都知道sscanf拷贝完成后,还会在str的后面加上一个null字符,但如果没有一个字符满足条件,str将保持原值! 。这个实验也说明了,如果不使用%*过滤掉前面不需要的字符,你永远别想取得中间的字符。

实验8:

sscanf( "AAAaaaBC=" , "%*[A-Z]%*[a-z]%[^a-z=]" , str) ; ---------str的值为 "BC\0!!!!!!!"

这是一个综合实验,但这个实验的目的不是帮我们复习前面所学的知识,而是展示两个值得注意的地方:

注意1:%只能使用一次,但%*可以使用多次,比如在这个实验里面,先用%*[A-Z]过滤大写字母,然后用%*[a-z]过滤小写字母。

注意2:^后面可以带多个条件,且这些条件都受^的作用,比如^a-z=表示^a-z且^=(既不是小写字母,也不是等于号)。

实验9:

首先,%*[^0-9]过滤前面非数字的字符,然后用%i把数字字符转换成int型的整数,拷贝到变量k,注意参数必须使用k的地址。

第二篇:

sscanf是可以直接用正则表达式的。

第一个参数是源字符串。第三个及以后的参数是可变参数列表,用于接收解析出来之后的值。最有玄机的是第二个参数,也就是所谓的format。我们知道prinft,sprintf,scanf,sscanf这四个函数(以及相应的双字节版本)都接收一个名为format的字符串作为参数,以便对输入输出做格式化,比如

  1. sscanf(“12”, "%s", str)

可以把字符串"12"(念做一二)格式化成10进制数12(念做十二),并赋值给in,而

  1. sscanf(“12”, "%d", &in)

可以把字符串"12"格式化成字符串12,并拷贝到str指向的内存区域。但是你可真不一定了解,format里是可以直接用正则表达式的!

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. static void sscanf_test(void)  
  6. {  
  7.     int ret;  
  8.     char *string;  
  9.     int  digit;  
  10.     char buf1[255];  
  11.     char buf2[255];  
  12.     char buf3[255];  
  13.     char buf4[255];  
  14.   
  15.     /*1.最简单的用法*/  
  16.     string = "china beijing 123";  
  17.     ret = sscanf(string, "%s %s %d", buf1, buf2, &digit);  
  18.     printf("1.string=%s\n", string);  
  19.     printf("1.ret=%d, buf1=%s, buf2=%s, digit=%d\n\n", ret, buf1, buf2, digit);  
  20.     /* 
  21.     执行结果: 
  22.     1.ret=3, buf1=china, buf2=beijing, digit=123 
  23.     可以看出,sscanf的返回值是读取的参数个数 
  24.     */  
  25.   
  26.     /*2.取指定长度的字符串*/  
  27.     string = "123456789";  
  28.     sscanf(string, "%5s", buf1);  
  29.     printf("2.buf1=%s\n\n", buf1);  
  30.     /* 
  31.     **执行结果: 
  32.     **2.buf1=12345 
  33.     */  
  34.   
  35.     /*3.取到指定字符为止的字符串*/  
  36.     string = "123/456";  
  37.     sscanf(string, "%[^/]", buf1);  
  38.     printf("3.string=%s\n", string);  
  39.     printf("3.buf1=%s\n\n", buf1);  
  40.     /* 
  41.     **执行结果: 
  42.     **3.buf1=123 
  43.     */  
  44.   
  45.     /*4.取到指定字符集为止的字符串*/  
  46.     string = "123abcABC";  
  47.     sscanf(string, "%[^A-Z]", buf1);  
  48.     printf("4.string=%s\n", string);  
  49.     printf("4.buf1=%s\n\n", buf1);  
  50.     /* 
  51.     **执行结果: 
  52.     **4.buf1=123abc 
  53.     */  
  54.   
  55.     /*5.取仅包含指定字符集的字符串*/  
  56.     string = "0123abcABC";  
  57.     sscanf(string, "%[0-9]%[a-z]%[A-Z]", buf1, buf2, buf3);  
  58.     printf("5.buf1=%s, buf2=%s, buf3=%s\n\n", buf1, buf2, buf3);  
  59.     /* 
  60.     **执行结果: 
  61.     **5.buf1=123, buf2=abc, buf3=ABC 
  62.     */  
  63.   
  64.     /*6.获取指定字符中间的字符串*/  
  65.     string = "ios<android>wp7";  
  66.     sscanf(string, "%*[^<]<%[^>]", buf1);  
  67.     printf("6.buf1=%s\n\n", buf1);  
  68.     /* 
  69.     **执行结果: 
  70.     **6.buf1=android 
  71.     */  
  72.   
  73.     /*7.指定要跳过的字符串*/  
  74.     string = "iosVSandroid";  
  75.     sscanf(string, "%[a-z]VS%[a-z]", buf1, buf2);  
  76.     printf("7.string=%s\n", string);  
  77.     printf("7.buf1=%s, buf2=%s\n\n", buf1, buf2);  
  78.     /* 
  79.     **执行结果: 
  80.     **7.buf1=ios, buf2=android 
  81.     */  
  82.   
  83.     /*8.分割以某字符隔开的字符串*/  
  84.     string = "android-iphone-wp7";  
  85.     /* 
  86.     **字符串取道'-'为止,后面还需要跟着分隔符'-', 
  87.     **起到过滤作用,有点类似于第7点 
  88.     */  
  89.     sscanf(string, "%[^-]-%[^-]-%[^-]", buf1, buf2, buf3);  
  90.     printf("8.string=%s\n", string);  
  91.     printf("8.buf1=%s, buf2=%s, buf3=%s\n\n", buf1, buf2, buf3);  
  92.     /* 
  93.     **执行结果: 
  94.     **8.buf1=android, buf2=iphone, buf3=wp7 
  95.     */  
  96.   
  97.     /*9.提取邮箱地址*/  
  98.     string = "Email:beijing@sina.com.cn";  
  99.     sscanf(string, "%[^:]:%[^@]@%[^.].%s", buf1, buf2, buf3, buf4);  
  100.     printf("9.buf1=%s, buf2=%s, buf3=%s, buf4=%s\n\n", buf1, buf2, buf3, buf4);  
  101.     /* 
  102.     **执行结果: 
  103.     **9.buf1=Email, buf2=beijing, buf3=sina, buf4=com.cn 
  104.     */  
  105.   
  106.     /*10.过滤掉不想截取或不需要的字符串--补充, 
  107.     **在%号后面加一*号,代表过滤这个字符串,不读取 
  108.     */  
  109.     string = "android iphone wp7";  
  110.     sscanf(string, "%s %*s %s", buf1, buf2);  
  111.     printf("10.string=%s\n", string);  
  112.     printf("10.buf1=%s, buf2=%s\n\n", buf1, buf2);  
  113.     /* 
  114.     **执行结果: 
  115.     **10.android wp7 
  116.     */  
  117. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值