sscanf的字符串格式化用法


     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:wxy@zte.com.cn;

%*[^:]:%[^;]

wxy@zte.com.cn

email:wxy@zte.com.cn

%*[^:]:%s

wxy@zte.com.cn

wxy@zte.com.cn

%[^@]%*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格式化的目的是“截取”,而正則表達式的目的是“匹配”,不能完全等同。

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM