使用正则表达式解析URL

  在开发http相关程序时,经常会碰到从网络链接URL中提取协议名、服务器、路径等目标对象,如果仅使用C/C++字符串操作函数,那么则显得有点麻烦且代码不易维护,其实关于文本内容的解析工作,都可优先考虑使用正则表达式库来解决处理,关于C++方面的正则库也有很多种,如atl,pcre,boost等。下面就使用boost中的regex来解析URL提取协议名、服务器、路径为目标说明其用法。
   (1)协议名:在URL可有可无,如果有时则后面必跟着://,如果没有,则默认为使用http协议。通常还有其它的协议如https、ssl、ftp、mailto 等。因此匹配 这一部分的正则表达式应该是 (?:(mailto|ssh|ftp|https?)://)? 注意 这个表达式本身 捕获了 协议名,但 不包括 ://
   (2)服务器:这一部分或 名称 ,如 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子串。
  (3)路径:这一部分最简单的形式为(/.*)?,更精确的形式为/[^.!,?;"'<>()\[\]{}\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 {
 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
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 {
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
 8 int  _tmain( int  argc, _TCHAR *  argv[])
 9 {
10    using namespace boost;
11
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
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
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
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
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
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
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
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
70    return 0;
71}

   对URL的解析,因时间有限,本文所述不尽详细,只是略作分析,以点带面,更多的精确匹配则依赖于实际的应用需求。


转自http://www.cppblog.com/qinqing1984/archive/2011/11/27/161035.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值