这两个函数都是可变参数,函数原型分别为:
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 0x64 123456 1234567 1234567abcdedfg 1234567abcdedfg 1234 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/123abc@456\" ,\"%*[^/]/%[^@]\" ,buf ); // 取/ 和@ 之间的字符串 printf (\"%sn\" , buf ); sscanf (\"ab/c1@23a/bcd@456\" ,\"%*[^/]/%[^@] %*[^/]/%[^@]\" ,buf , c ); // 取/ 和@ 之间的字符串 printf (\"%s %sn\" , buf , c ); sscanf (\"hel/lo ,world ! Fine !\" , \"%*[^/]/%[^@]\" , buf ); // 缺省@ printf (\"%sn\" , buf ); sscanf (\"he/llo ,wor/ld ! /Fine@ !\" , \"%*[^/]/%[^@]\" , 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 ; } 输出结果如下: Fine world 100 123abc c1 bcd lo ,world ! Fine ! llo ,wor/ld ! /Fine 123Aa321BW%abcFG#abcde 123Aa321BWabcFGab 12939488567 123a321b 123 123A321BW 1234567abcdedf 123 2014 :05 :18 - 2014 :06 :30 2014 :05 :18 - 2014 :06 :30 2014 5 1234 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.6 1 张三 34 55.600000 依次输入字符、字符串、整数和实数: 3 Hob 23 45 3 Hob 23 45.000000