這兩個函數都是可變參數,函數原型分別為:
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