这两个函数都是可变参数,函数原型分别为:
int scanf( const char *format [,argument]... );int sscanf (const char *str,const char * format, ...);
sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。
【例20.22】下面是一个进行简单加减法的程序,编译时没有报错,但运行时结果错误,请分析原因并改正。
#include <stdio.h>#include <stdlib.h>int main(){ char op; int result=0; int value; while(1) { printf(/"进入运算符和数值:/"); scanf(/"%c %d/",&op,&value); switch(op){ case /'+/': result+=value; break; case /'-/': result-=value; break; case /'q/': exit(0); default: printf(/"错误操作数n/"); break; } printf(/"结果:%dn/",result); }}
【解答】赋值语句没有错误,但并不能正确读取输入。假如第一次输入“+8”,这一次能正确读入,但在8之后输入一个换行符,这个符号将被第2次读字符时读入op作为操作数,从而造成switch进入default,发生错误。
正如过去分析的那样,将读字符放在最后可以克服这种问题,即
scanf(/"%d %c/",&value,&op);
但这需先输入数值,再输入操作数。例如原来的顺序是“+8”,现在是“8+”。这样也可以被接收,但要退出来则需要输入数字和q的组合,这很不符合使用的习惯。当然也可以分别使用一个scanf语句接收数据。
用字符数组line,再用fgets函数和sscanf函数相配合,能够满足本题的要求。
//改正的程序#include <stdio.h>#include <stdlib.h>int main(){ char op; char line[32]; int result=0; int value; while(1) { printf(/"进入运算符和数值:/"); fgets(line,sizeof(line),stdin); sscanf(line,/"%c %d/",&op,&value); switch(op){ case /'+/': result+=value; break; case /'-/': result-=value; break; case /'q/': exit(0); default: printf(/"错误操作数n/"); break; } printf(/"结果:%dn/",result); }}
运行示范如下:
进入运算符和数值:+ 8结果:8进入运算符和数值:+ 7结果:15进入运算符和数值:- 6结果:9进入运算符和数值:q
20.5.1 sscanf函数的使用方法
函数原型为:
int sscanf (const char *str, const char *format, …);
sscanf以固定常量字符串str为输入源,格式控制符format参照scanf的格式控制符的使用规则(但更复杂一些),后面可变参数表,用法参照scanf的变量地址表的用法。
由此可见,sscanf会将参数str的字符串根据参数format字符串来转换并格式化数据。格式转换形式参考scanf,转换后的结果存于对应的参数内,参数的形式也是使用参数地址。
返回值:成功则返回参数数目,失败则返回-1,错误原因存于errno中。返回0表示失败,否则表示正确格式化数据的个数。例如语句
sscanf(str,/"%d%d%s/", &i,&i2, &s);
将从str中顺次读入2个整数给整型变量i1和i2,读入一个字符串给字符串变量s。如果三个都读入成功则返回3,如果只读入了第一个整数到i就返回1,则说明将无法从str读入第二个整数。
字符串str含有字符和数字。使用时可以直接使用“we 123”的形式,也可以用字符串变量。
format的形式比较复杂,可以是一个或多个{%[*][width][{h|l|I64|L}]type|/'/'|/'t/'|/'n/'|非%符号}格式化符号。下面简单解释一下它们的含义。
1.格式含义
(1)*亦可用于格式中(即%*d和%*s),加了星号(*)表示跳过此数据不读入(也就是不把此数据读入参数中)。
(2){a|b|c}表示a、b、c中选一,[d]表示可以有d也可以没有d。
(3)width表示读取宽度。
(4){h|l|I64|L}参数的size,通常h表示单字节size,I表示2字节size,L表示4字节size(double例外),l64表示8字节size。
(5)type就是%s,%d之类的格式。
(6)%*[width][{h|l|I64|L}]type表示满足该条件的将被过滤掉,不会向目标参数中写入值。
2.支持的集合操作
(1)%[a-z]表示匹配a到z中任意字符(尽可能多地匹配)。
(2)%[aB/']匹配a、B、/'中一员。
(3)%[^a]匹配非a的任意字符。
20.5.2 sscanf函数用法举例
【例20.23】典型用法举例。
#include <stdio.h>int main(){ char buf[256]; int a,b; sscanf(/"1234567 100/", /"%s%d/", buf,&a); //取字符串和数字 printf(/"%s %#xn/", buf,a); //输出字符串和16进制数字 sscanf(/"1234567 /", /"%6s/", buf); //取6个字符 printf(/"%sn/", buf); sscanf(/"1234567 abcdedfg/", /"%[^ ]/", buf); //滤除空格 printf(/"%sn/", buf); sscanf(/"1234567abcdedfgABCDEFG/", /"%[1-9a-z]/", buf); //取数字和小写字母 printf(/"%sn/", buf); sscanf(/"1234567abcdedfgABCDEFG/", /"%[^A-Z]/", buf); //滤除大写字母 printf(/"%sn/", buf); sscanf(/"1234 100 9 15/", /"%s%*d%d%d/", buf,&a,&b); //*跳过数字100 printf(/"%s %#o %#xn/", buf,a,b); //输出使用标志# return 0;}
程序运行结果如下:
1234567 0x6412345612345671234567abcdedfg1234567abcdedfg1234 011 0xf
【例20.24】对比各种用法的举例。
#include <stdio.h>int main( ){ char buf[256],c[16],c2; int a,b; sscanf(/"hello,world! Fine!/", /"%*s%4s/", buf); //仅取第2个字串的前4个字符 printf(/"%sn/", buf); sscanf(/"hello, world! Fine!/", /"%*s%5s/", buf); //仅取world printf(/"%sn/", buf); sscanf(/"123,456! 100/", /"%*s%d/", &a); //仅取数字100 printf(/"%dn/", a); sscanf(/"abc/[email protected]/",/"%*[^/]/%[^@]/",buf); //取/和@之间的字符串 printf(/"%sn/", buf); sscanf(/"ab/[email protected]/[email protected]/",/"%*[^/]/%[^@] %*[^/]/%[^@]/",buf, c); //取/和@之间的字符串 printf(/"%s %sn/", buf, c); sscanf(/"hel/lo,world! Fine!/", /"%*[^/]/%[^@]/", buf); //缺省@ printf(/"%sn/", buf); sscanf(/"he/llo,wor/ld! /[email protected]!/", /"%*[^/]/%[^@]/", buf); //使用/"//"字符 printf(/"%sn/", buf); sscanf(/"123Aa321BW%abcFG#abcde/",/"%s/", buf); //全部字符 printf(/"%sn/", buf); sscanf(/"123Aa321BWabcFGab&cde/",/"%[1-9a-zA-Z]/", buf); //遇到其他符号结束 printf(/"%sn/", buf); sscanf(/"12939488567abcd35edfg89ABCDEFG/", /"%[1-9]/", buf); //只能提取相邻数字 printf(/"%sn/", buf); sscanf(/"123a321bWabcFGabcde/",/"%[a-z1-9]/", buf); //遇到第1个大写字母为止 printf(/"%sn/", buf); sscanf(/"123a321bWabcFGabcde/",/"%[A-Z1-9]/", buf); //遇到第1个小写字母为止 printf(/"%sn/", buf); sscanf(/"123A321BWabcFGabcde/",/"%[1-9A-Z]/", buf); //遇到第1个小写字母为止 printf(/"%sn/", buf); //滤除第1个标志之后的所有字符 sscanf(/"1234567abcdedfBgABWZCDEFGBA/", /"%[^A-Z]/", buf); //滤除B后所有字母 printf(/"%sn/", buf); sscanf(/"123D4567abcdedfBgABWZCDEFGBA/", /"%[^A-Z]/", buf); //滤除D后所有字母 printf(/"%sn/", buf); sscanf(/"2014:05:18 - 2014:06:30/", /"%s %c %s/", buf,&c2,c); //空格区分 printf(/"%s %c %sn/", buf,c2, c); sscanf(/"2014:05:18 - 2014:06:30/", /"%s - %s/", buf,c); //空格区分 printf(/"%s %c %sn/", buf,c2, c); sscanf(/"2014:05/", /"%d:%d/", &a, &b); //空格区分 printf(/"%d %dn/", a, b); sscanf(/"1234 100 9 15/", /"%s%*d%d%d/", buf,&a,&b); //*跳过数字100 printf(/"%s %#o %#xn/", buf,a,b); //输出使用标志# return 0;}输出结果如下:Fineworld100123abcc1 bcdlo,world! Fine!llo,wor/ld! /Fine123Aa321BW%abcFG#abcde123Aa321BWabcFGab12939488567123a321b123123A321BW1234567abcdedf1232014:05:18 - 2014:06:302014:05:18 - 2014:06:302014 51234 011 0xf
【例20.25】接收输入的例子。
#include <stdio.h>int main(){ char buf[256],c[16],c2; int a=0,i=0; double b=0; for(i=0;i<2;i++) { printf(/"依次输入字符、字符串、整数和实数:/"); fgets(buf,sizeof(buf),stdin); sscanf(buf,/"%c %s %d %lf/",&c2, c,&a, &b); printf(/"%c %s %d %lfn/",c2,c,a,b); } return 0;}
程序运行示范如下:
依次输入字符、字符串、整数和实数:1 张三 34 55.61 张三 34 55.600000依次输入字符、字符串、整数和实数:3 Hob 23 453 Hob 23 45.000000