在开发HTTP相关程序时,经常会碰到从网络链接URL中提取协议名、服务器、路径等目标对象,如果使用C/C++字符串操作函数,那么则显得有点麻烦且代码不易维护,其实关于文本内容的解析工作,都可优先考虑使用正则表达式库来解决处理,C++方面的正则库也有很多种,如atl、pcre、boost。下面就使用boost中的regex来解析URL提取协议名、服务器、路径为目标说明其用法。
协议名 可有可无,如果有时则后面必跟着://,如果没有,则默认为使用http协议。通常还有其它的协议如https、ssl、ftp、mailto等。因此匹配协议名的正则表达式应该是(?:(mailto|ssh|ftp|https?)://)?,注意这个表达式本身捕获了协议名,但不包括://。
服务器 或是域名,如www.csdn.net;或是IP地址,如192.168.1.1,可带端口号,如192.168.1.1:8080。匹配域名的正则表达式为(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+(?:com|net|edu|biz|gov|org|in(?:t|fo)|(?-i:[a-z][a-z])),表达式"(?:com|net|edu|biz|gov|org|in(?:t|fo)"匹配了com、net、edu、biz、gov、org、int、info等常见的域名,而(?-i:[a-z][a-z])匹配了国家代码,而且只允许小写为合法的,如www.richcomm.com.cn。匹配IP要尽量精确,考虑到IP每部分应为数字且范围在0-255之间,因此表达式应为(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])。注意以上域名或IP的正则式本身不捕获它们,这是为了留在后面作为整体捕获。
端口号的正则表达式为(?::(\d{1,5}))?,这里限制了端口号为1至5位的数字,更精确的匹配如要求在某范围如[1024,65535]间则可参考以上IP正则模式。综上所得,匹配服务器的正则表达式为((?:(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+(?:com|net|edu|biz|gov|org|in(?:t|fo)|(?-i:[a-z][a-z]))|(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])))(?::(\d{1,5}))?,这个正则式作为整体捕获了域名或IP,及端口号(若有),如www.csdn.net,则得到www.csdn.net和空(没有端口,http默认为80,https默认为443)子串;192.168.1.1:8080则得到192.168.1.1和8080子串。
路径 最简单的形式为(/.*)?,更精确的形式为/[^.!,?;"'<>()\[\]{}\s\x7F-\xFF]*(?:[.!,?]+[^.!,?;"'<>()\[\]{}\s\x7F-\xFF]+)*。
以上所有正则表达式均为ascii字符集,对于unicode字符集则在其前加L即可。
为方便使用,封装成了两个自由模板函数,如下所示
1
template<typename charT>
2
inline bool boost_match(const charT* pattern,const charT* text,unsigned int flags=boost::regex::normal,boost::match_results<const charT*>* result=NULL)
3![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
4
boost::basic_regex<charT,boost::regex_traits<charT> > expression(pattern,flags);
5
if(NULL==result)
6
return boost::regex_match(text,expression);
7
return boost::regex_match(text,*result,expression);
8
}
9![](/Images/OutliningIndicators/None.gif)
10
template<typename charT>
11
inline bool boost_search(const charT* pattern,const charT* text,unsigned int flags=boost::regex::normal,boost::match_results<const charT*>* result=NULL)
12![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
13
boost::basic_regex<charT,boost::regex_traits<charT> > expression(pattern,flags);
14
if(NULL==result)
15
return boost::regex_search(text,expression);
16
return boost::regex_search(text,*result,expression);
17
}
测试示例如下
1
static const string protocol = "(?:(mailto|ssh|ftp|https?)://)?";
2
static const string hostname = "(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)+(?:com|net|edu|biz|gov|org|in(?:t|fo)|(?-i:[a-z][a-z]))";
3
static const string ip = "(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5])";
4
static const string port = "(?::(\\d{1,5}))?";
5
static const string path = "(/.*)?";
6
static const string pattern = protocol + "((?:" + hostname + "|" + ip + "))" + port + path;
7![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
8
int _tmain(int argc, _TCHAR* argv[])
9![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
10
using namespace boost;
11![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
12
//形式1: 带协议名,服务器为名称,不带端口号
13
bool ret;
14
string text = "http://www.cppblog.com/qinqing1984";
15
boost::cmatch what;
16
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
17
assert(ret);
18
assert(what[1].str()=="http");
19
assert(what[2].str()=="www.cppblog.com");
20
assert(what[3].str()=="");
21
assert(what[4].str()=="/qinqing1984");
22![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
23
//形式2: 不带协议名,服务器为名称,带端口号
24
text = "www.cppblog.com:80/qinqing1984";
25
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
26
assert(ret);
27
assert(what[1].str()=="");
28
assert(what[2].str()=="www.cppblog.com");
29
assert(what[3].str()=="80");
30
assert(what[4].str()=="/qinqing1984");
31![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
32
//形式3: 不带协议名,服务器为名称,不带路径
33
text = "www.cppblog.com:80";
34
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
35
assert(ret);
36
assert(what[1].str()=="");
37
assert(what[2].str()=="www.cppblog.com");
38
assert(what[3].str()=="80");
39
assert(what[4].str()=="");
40![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
41
//形式4: 协议为https,服务器为IP,带端口号
42
text = "https://192.168.1.1:443/index.html";
43
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
44
assert(ret);
45
assert(what[1].str()=="https");
46
assert(what[2].str()=="192.168.1.1");
47
assert(what[3].str()=="443");
48
assert(what[4].str()=="/index.html");
49![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
50
//形式5: 端口超过5位数
51
text = "ftp://192.168.1.1:888888";
52
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
53
assert(!ret);
54![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
55
//形式6: 没有协议名
56
text = "//192.168.1.1/index.html";
57
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
58
assert(!ret);
59![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
60
//形式7: 没有服务器
61
text = "http:///index.html";
62
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
63
assert(!ret);
64![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
65
//形式8: 不合法的服务器
66
text = "cppblog/index.html";
67
ret=boost_match(pattern.c_str(),text.c_str(),regex::icase|regex::perl,&what);
68
assert(!ret);
69![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
70
return 0;
71
}
对URL的解析,因时间有限,本文所述不尽详细,只是略作分析,以点带面,更多的精确匹配则依赖于实际的应用需求。
posted on 2011-11-27 17:22
春秋十二月 阅读(7789)
评论(5) 编辑 收藏 引用 所属分类:
Opensrc