1
分类: LINUX
2010-04-28 22:41:25
出处:http://blog.csdn.net/phil2036/archive/2009/05/27/4221704.aspx
感谢原文作者分享。
在看这篇文章之前建议你先了解一下什么是正则表达式,具体不重复了,google一下能出一大堆,不过这篇文章比较好,贴个url ,作者说30分钟能看完的不是地球人,但是我15分钟就看完了还记住了90%以上,好吧我来自火星,玩笑到此结束,下面切入正题。
本文的命题源自于这样的一个url,protocol://ip:port/chn/mode,比如说,dv://192.168.1.253:65001/1/1。如果我要解析出来各个部分,并把它们保存到各自的变量里面要怎么做?传统的C程序员会说用strtok慢慢取得各个部分,对于要转成int的用atoi,部分C++程序员也许会放弃strtok转用std::string当中的find系列成员函数(find/find_first_of...)。是的,我以前也是这么干的,两种方法都用过,当然也包括MFC里的CString的相关成员函数。现在,一种全新的方法源自于sscanf这个C语言函数。
无论你用C还是C++,sscanf和scanf这两个函数都不会太陌生,%d %s %f甚至%02d %.2f这些东西你也很熟,看了上面我说的正则表达式,也许你会写出这么一个正则表达式:[a-zA-Z]+://(\d{1,3}\.){3}(\d{1,3}):\d{1,5}/\d{1,}/\d,没错,这个正则表达式的IP部分有点问题,但是为了描述简单,暂时就这么用了。激动人心的时候到了,我要告诉你的是scanf以及sscanf实际上是支持部分正则表达式的,当然即使是到现在我依旧不敢肯定这个部分是否是ANSI的一部分,但是我的VS2005上没有问题,朋友的VS2003上也没有问题,至于VC6实在太古老暂时找不到测试。但是顺便说一句,VS2005上你用sscanf或者scanf他会出警告的,他说这两个函数不安全建议你用sscanf_s和scanf_s,我试了下推荐的两个函数又不支持这个功能了,比较囧了。
好了,具体说说sscanf的这个扩展功能吧(暂且这么叫)。sscanf提供的这个扩展功能其实并不能真正称为正则表达式,因为他的书写还是离不开%,而且也很局限。但是作为处理我上面说的url已经是绰绰有余了。sscanf的这个扩展功能支持[]表示支付范围,{}表示重复次数,^表示取非,*表示跳过。所以上面这个url的解析可以写成下面这个样子:
char url[] = "dv://192.168.1.253:65001/1/1"
sscanf(url,"%[^://]%*c%*c%*c%[^:]%*c%d/%d/%d",protocol,ip,&port,&chn,&type);//这里的参数需要传地址进去。这里面port等整型的声明为int prot...
解释一下
先取得一个最长的字符串,但不包括字串://,于是protocol="dv\0";
然后跳过三个字符(%*c),其实就是跳过://
接着取一个字符串不包括字符串:,于是ip=192.168.1.253,这里简化处理了,IP就当个字符串来弄,而且不做检查
然后跳过冒号取端口到port,再跳过/取通道号到chn,再跳过/取码流类型到type。
是不是觉得还不过瘾?我也觉得,接着举例。
sscanf("Phil\nChang","%[^\n]%*c%s",first_name,last_name);
解释:跳过一个换行符,取first_name和last_name
类似上面的sscanf("phil2360@gmail.com","%[^@]%*c%s",user_name,host);
基本上,这个东西用的不多,但是有时候很实用,让你的代码会很简洁,但是相比正则表达式,功能又显得简陋很多,怎么用,就看个人而定了,还是那句话,不是很确定这东西是不是ANSI的,所以不考虑移植又觉得很简洁那么用吧,考虑移植那么请三思,至于正则表达式,用regex或者boost库吧,哦,我说的是C++,C#本来就带了的!
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/phil2036/archive/2009/05/27/4221704.aspx
以下转自chinaunix:
原文地址:http://blog.chinaunix.net/u/21684/showart_499274.html
头文件 #include(stdio.h)
定义函数 int sscanf (const char *str,const char * format,........);
函数说明 sscanf()会将参数str的字符串根据参数format字符串来转换并格式化数据。格式转换形式请参考scanf()。转换后的结果存于对应的参数内。
返回值 成功则返回参数数目,失败则返回-1,错误原因存于errno中。
|
函数原型:
int scanf( const char *format [,argument]... );
其中的format可以是一个或多个 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | 非%符号},
注:{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。
width:宽度,一般可以忽略,用法如:
const char sourceStr[] = "hello, world";
char buf[10] = {0};
sscanf(sourceStr, "%5s", buf); //%5s,只取5个字符
cout << buf<< endl;
结果为:hello
{h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。
type :这就很多了,就是%s,%d之类。
特别的:
%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值。如:
const char sourceStr[] = "hello, world";
char buf[10] = {0};
sscanf(sourceStr, "%*s%s", buf); //%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了
cout << buf<< endl;
结果为:world
支持集合操作:
%[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配)
%[aB'] 匹配a、B、'中一员,贪婪性
%[^a] 匹配非a的任意字符,贪婪性
|
搜集一些特殊用法:
|
参考:
http://blog.csdn.net/beingstudio/articles/1806661.aspx
http://hi.baidu.com/lbird/blog/item/07e09c8282dbe992f703a6b0.html
http://hi.baidu.com/yinjianren/blog/item/28dce5ca75e30585c9176896.html