讀古今文學網 > 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