名著阅读 > C语言解惑 > 20.5 scanf和sscanf函数 >

20.5 scanf和sscanf函数

这两个函数都是可变参数,函数原型分别为:


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