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%*[^=]=%s10.46.44.40
Name = Yuan%*[^=]=%*[ \t]%s Yuan
email:wxy@zte;%*[^:]:%[^;]wxy@zte
email:wxy@zte;%*[^:]:%[^;]wxy@zte
email:wxy@zte%*[^:]:%s wxy@zte
wxy@zte%[^@]%*c%s串1:wxy;串2:zte
IpAddr=10.46.44.40%[^=]=%s串1:IpAddr;串2:10.46.44.40
1hello234world51%[^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]"格式串;
若不确定⾏⾸有⽆空⽩字符,可先跳过空⽩字符:
1char szName[] = "Name = Yuan";
2char 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*****************************************************************************/
6static 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
15if(strncasecmp(pucFarEndIpInfo, strUdpHead, ucUdpUrlLen) != 0)
16 {
17 OmciLog(LOG_CES,"[%s]Cannot Parse FarEndIpInfo(%s)!\n\r", __FUNCTION__, pucFarEndIpInfo);
18return S_ERROR;
19 }
20
21 INT8U ucMaxUrlLen = ucUdpUrlLen + STR_IPV4_MAX_LEN; //避免未配置端⼝时陷⼊死循环(infi
nite loop)
22for(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
30return retCode;
31 }
使⽤sscanf格式化则更为简单:
1char szUrl[] = "udp://192.168.100.221:5000";
2char 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】提取数字
1char szDig[]="10,22m,Z86,,880;555:666.";
2int dwIdx = 0, dwVal = 0, dwSize = 0;
3while(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格式化的⽬的是“截取”,⽽正则表达式的⽬的是“匹配”,不能完全等同。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论