sscanf格式(轉)
文章轉載自:https://www.cnblogs.com/clover-toeic/p/3727028.html
sscanf()為C語言標準庫函式,用於從指定字串中讀入與指定格式相符的資料。函式原型宣告在stdio.h標頭檔案中:
int sscanf(const char *str, const char *format, ...); |
該函式根據引數format(格式化字串)來轉換引數str指向的字串,轉換後的結果存於對應的可變引數內。其返回值為按照指定格式轉換符成功讀入且賦值的可變引數數目(若發生匹配錯誤而部分成功,該數目會小於指定的引數數目,甚至為0)。若首次成功轉換或錯誤匹配發生前輸入已結束(如str為空字串),則返回EOF。發生讀取錯誤時也返回EOF,且設定錯誤碼errno(如format為空指標時返回EOF並設定errno為EINVAL)。可見,通過比較該函式的返回值與指定的可變引數數目,可判斷格式轉換是否成功。
format可為一個或多個{%[*] [width] [{h | l | L}]type | ' ' | '\t' | '\n' | 非%符號}格式轉換符。集合中{a|b|c}表示格式符a、b、c任選其一。以中括號括起來的格式符可選。%與type為必選,所有格式符必須以%開頭。
以下簡要說明各格式符的含義:
1) 賦值抑制符'*'表明按照隨後的轉換符指示來讀取輸入,但將其丟棄不予賦值(“跳過”)。抑制符無需相應的指標可變引數,該轉換也不計入函式返回的成功賦值次數。
%*[width] [{h | l | L}]type 表示滿足該條件的字元被過濾掉,不會向目標引數中賦值。
2) width表示最大讀取寬度。當讀入字元數超過該值,或遇到不匹配的字元時,停止讀取。多數轉換丟棄起始的空白字元。這些被丟棄的字元及轉換結果新增的空結束符('\0')均不計入最大讀取寬度。
3) {h | l | L}為型別修飾符。h指示輸入的數字數值以short int或unsigned short int型別儲存;hh指示輸入以signed char或unsigned char型別儲存。l(小寫L)指示輸入以long int、unsigned long int或double型別儲存,若與%c或%s結合則指示輸入以寬字元或寬字串儲存;ll等同L。L指示輸入以long long型別儲存。
4) type 為型別轉換符,如%s、%d。
此外,還有兩種特殊的格式符:
1) []:字元集合。[]表示指定的字元集合匹配非空的字元序列;^則表示過濾。該操作不會跳過空白字元(空格、製表或換行符),因此可用於目標字串不以空白字元分隔時。[]內可有一到多個非^字元(含連字元'-'),且無順序要求。%[a-z]表示匹配a到z之間的任意字元,%[aB-]匹配a、B、-中的任一字元;%[^a]則匹配非a的任意字元,即獲取第一個a之前的(不為a的)所有字元。^可作用於多個條件,如^a-z=表示^a-z且^=(既非小寫字母亦非等號)。空字符集%[]和%[^]會導致不可預知的結果。
使用[]時接收輸入的引數必須是有足夠儲存空間的char、signed char或unsigned char陣列。[]也是轉換符,故%[]後無s。
%[^]的含義和用法與正則表示式相同,故sscanf函式某種程度上提供了簡單的正則表示式功能。
2) n:至此已讀入值(未必賦值)的等價字元數,該數目必須以int型別儲存。如"10,22"經過"%d%*[^0-9]%n"格式轉換後,%n對應的引數值為3(雖然','未參與賦值)。
'n'並非轉換符,儘管它可用'*'抑制。C標準聲稱,執行%n指令並不增加函式返回的賦值次數;但其勘誤表中的描述與之矛盾。建議不要假設%n對返回值的影響。
下表列舉sscanf函式常見的格式化用法:
原字串 |
格式化字串 |
結果字串 |
helloworld |
%s |
helloworld |
hello, world |
%s |
hello, |
hello, world |
%4s |
hell |
hello, world |
%*s%s |
world(若原字串無空格則得到空串) |
hello, world |
%[^ ] |
hello, |
hello, world |
%[a-z] |
hello |
12345helloWORLD |
%[1-9a-z]或%[a-z1-9] |
12345hello |
12345helloWORLD |
%[^A-Z] |
12345hello |
12345helloWORLD |
%[^a-f] |
12345h |
12345helloWORLD |
%*[1-9]%[a-z] |
hello |
12345helloWORLD |
%[a-z] |
(空串,需先過濾前面不需要的字元) |
12345helloWORLD= |
%*[1-9]%*[a-z]%[^a-z=] |
WORLD |
12345/hello@world |
%*[^/]/%[^@] |
hello |
IpAddr=10.46.44.40 |
%*[^=]=%s |
10.46.44.40 |
Name = Yuan |
%*[^=]=%*[ \t]%s |
Yuan |
email:[email protected]; |
%*[^:]:%[^;] |
|
email:[email protected] |
%*[^:]:%s |
|
%[^@]%*c%s |
串1:wxy;串2:zte.com.cn |
|
IpAddr=10.46.44.40 |
%[^=]=%s |
串1:IpAddr;串2:10.46.44.40 |
1hello234world5 |
1%[^2]234%[^5] |
串1:hello;串2:world |
Michael/nWang |
%[^/n]%*c%*c%s |
串1:Michael;串2:Wang |
Michael\nWang |
%[^\n]%*c%s |
串1:Michael;串2:Wang |
13:10:29-13:11:08 |
%[0-9,:] - %[0-9,:] |
串1:13:10:29;串2:13:11:08 |
10.46.44.40 |
%d.%d.%d.%d |
串1:10;串2:46;串3: 44;串4: 40 |
此外,還有如下幾種用法:
【例1】讀入一行字串
因字串可能含空白字元,故不能直接使用%s;而gets函式存在溢位風險,不推薦使用。此時,可使用sscanf函式,格式化字串設為"%[^\n]%*c"。%*c用於跳過換行符\n,以便再次讀入下一行。
【例2】提取"Name = Yuan"中的"Name"
若行首有空白字元,可用"%*[ \t]%[^= \t]"格式串;
若不確定行首有無空白字元,可先跳過空白字元:
1 char szName[] = "Name = Yuan"; 2 char szResBuf[32] = {0}; 3 sscanf(szName+strspn(szName," \t"), "%[^= \t]", szResBuf);
【例3】分解URL
普通實現如下所示:
1 /***************************************************************************** 2 * 函式名稱:OaSplitPwFarEndIpInfo 3 * 功能描述:將遠端IP資訊分解為目的IP地址和埠號 4 * 注意事項:遠端IP資訊應形如'udp://192.168.100.221:5000' 5 *****************************************************************************/ 6 static FUNC_STATUS OaSplitPwFarEndIpInfo(INT8U *pucFarEndIpInfo, INT32U *dwDstUdpPort, INT8U *pucDstIpAddr) 7 { 8 FUNC_STATUS retCode = S_OK; 9 INT8U strUdpHead[] = "udp://"; 10 INT8U ucUdpUrlLen = strlen(strUdpHead); 11 INT8U ucIndex = 0; 12 13 CHECK_TRIPLE_POINTER(pucFarEndIpInfo, dwDstUdpPort, pucDstIpAddr, S_NULL_POINTER); 14 15 if(strncasecmp(pucFarEndIpInfo, strUdpHead, ucUdpUrlLen) != 0) 16 { 17 OmciLog(LOG_CES,"[%s]Cannot Parse FarEndIpInfo(%s)!\n\r", __FUNCTION__, pucFarEndIpInfo); 18 return S_ERROR; 19 } 20 21 INT8U ucMaxUrlLen = ucUdpUrlLen + STR_IPV4_MAX_LEN; //避免未配置埠時陷入死迴圈(infinite loop) 22 for(ucIndex = 0; (pucFarEndIpInfo[ucUdpUrlLen] != ':') && (ucUdpUrlLen < ucMaxUrlLen); ucIndex++) 23 { 24 pucDstIpAddr[ucIndex] = pucFarEndIpInfo[ucUdpUrlLen++]; 25 } 26 pucDstIpAddr[ucIndex] = '\0'; 27 28 *dwDstUdpPort = strtoul(&pucFarEndIpInfo[ucUdpUrlLen+1], NULL, 10); 29 30 return retCode; 31 }
使用sscanf格式化則更為簡單:
1 char szUrl[] = "udp://192.168.100.221:5000"; 2 char szProt[4] = {0}, szIp[32] = {0}; 3 unsigned int dwPort = 0; 4 sscanf(szUrl, "%[^://]%*c%*c%*c%[^:]%*c%d", szProt, szIp, &dwPort); 5 printf("szProt=%s, szIp=%s, dwPort=%d\n", szProt, szIp, dwPort);
【例4】提取數字
1 char szDig[]="10,22m,Z86,,880;555:666."; 2 int dwIdx = 0, dwVal = 0, dwSize = 0; 3 while(1 == sscanf(szDig+dwIdx, "%d%*[^0-9]%n", &dwVal, &dwSize)) 4 { 5 dwIdx += dwSize; 6 printf("dwIdx=%d, dwSize=%d, dwVal=%d\n", dwIdx, dwSize, dwVal); 7 }
上述實現稍加改造,即可用於處理某種字元分隔的數字串。
綜上,對於簡單的字串分析,採用sscanf函式處理比較簡潔。若字串比較複雜,則可藉助相應的正則表示式庫。需要注意,sscanf格式化的目的是“擷取”,而正則表示式的目的是“匹配”,不能完全等同。