这两个函数都是可变参数,函数原型分别为:
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