基于POSIX下REGEX库的文本URL过滤(C/C++语言)

来源:互联网 发布:crf算法 编辑:程序博客网 时间:2024/06/14 02:17

URL过滤报告

URL过滤需求分析:

输入为带中文、英文、标点符号、特殊符号等的字符串,输出也是字符串,但有以下要求:

1.把输入字符串中的网站URL(以“http://”开头)、文件URL(以“.xxx(若干个x)结尾”)的子串找到,并删除。

2.将其他的无关字符串原样保留,去除需要过滤的URL之后,直接拼接。

3.输出去掉所有URL,其他保持不变的字符串。

 

URL过滤技术分析:

1.将用户需求转换为以下正则表达式:

"(http://[a-zA-Z0-9./?%&_=-]+)|([a-zA-Z0-9_-]+\\.[a-zA-Z0-9]+)"

该正则表达式表示的意思为:

a.以http://开头,接着跟1个或多个包含在集合{a-zA-Z0-9./?%&_=-}的字符。

b.前面包含1个或多个{a-zA-Z0-9_-}集合中的字符,然后跟一个点(根据语法,需要写成\\.),最后跟1个或多个{a-zA-Z0-9_-}集合中的字符。

 

2.使用POSIX标准的regex库,包含regex.h头文件。首先在初始化过程中,使用函数regcomp编译正则表达式。之后在匹配过程中,使用函数regexec执行匹配,如果匹配到,则将匹配到的字符串删除。多次匹配删除,直到整个字符串处理完毕。最后调用regfree释放内存资源。

 

URL过滤测试:

测试配置:网络名为LT_Kdc_DE的开发机,3G Mem2G Swap内存,Intel(R) Xeon(R) CPU           E5606 @ 2.13GHz单处理器,CentOS Linux release 6.2 (Final)操作系统。

测试样本为包含中文、英文、标点符号、特殊符号的一篇短文,共有26行,5742个字符,3个网站URL1个文件URL

对文本的每行依次输入(共输入26次),输出结果能够正确的去除掉里面的URL,同时不影响其他无关字符串。

对文本的每行依次输入,反复循环执行100,000次正则匹配,共执行2,600,000次匹配调用的压力测试。十次压力测试的执行时间如下(单位ms):

1842

1838

1880

1842

1848

1849

1902

1849

1856

1860

平均值为2,600,000/1856.8ms,转换为1,400,256/s≈140w/s。基本满足性能需求。

 

关于正则表达式:

URL过滤规则中比较关键的是如何构建你所希望的URL正则表达式,网上有很多关于正则表达式的相关文章。在书写正则表达式集合中有个很需要注意的事项,特殊字符需要注意位置,在[]中想把]^-当成普通字符,是有要求:

]  要放在第一个

^  不能放在第一个

-  要放在第一个或者最后一个

 

扩展:

如果以后需要用到类似于URL过滤或文本过滤的模块,只需要修改正则表达式规则,及少许业务上的定制就行了。性能上,根据业务需求,优化一些字符串拷贝,删除以及内存分配的函数,可以进一步优化性能。如果追求更高的性能,可以采用自定义有限自动机的方式,根据业务来定制,但这样实现较为复杂,编程难度较大,并且容易出错。


源代码:

#include <sys/types.h>#include <sys/time.h>#include <regex.h>#include <string.h>#include <iostream>#include <fstream>#include <string>bool init(regex_t & reg){int status;int cflags = REG_EXTENDED; //支持正则表达式扩展标签// 过滤规则:以http://开头或以.xxx(若干个x)结尾的字符串const char * pattern = "(http://[a-zA-Z0-9./?%&_=-]+)|([a-zA-Z0-9_-]+\\.[a-zA-Z0-9]+)";  if((status = regcomp(&reg, pattern, cflags)) != 0)//编译正则表达式{std::cerr << "pattern compile error" << std::endl;char err_buff[1024];regerror(status, &reg, err_buff, 1024);std::cout << "error message:" << err_buff << std::endl;return false;}return true;}bool destroy(regex_t & reg){regfree(&reg);}bool url_filter(regex_t & reg, std::string & input, std::string & output) {int status;regmatch_t pmatch[1];const size_t nmatch = 1;// regex函数必须匹配'\0'结尾的字符串input.append(1,'\0');char * st = new char[input.length()];input.copy(st, input.length(), 0);//循环匹配多次while( st && (status = regexec(&reg, st, nmatch, pmatch, REG_NOTEOL)) != REG_NOMATCH){int num = pmatch[0].rm_eo - pmatch[0].rm_so;//std::cout << "<" << input.substr(pmatch[0].rm_so, num) << ">" << std::endl;//删除匹配到的字符串input.erase(pmatch[0].rm_so, num);input.copy(st, input.length(), 0);}// 删除掉我们添加的'\0'output = input.erase(input.length() - 1, 1);return true;}// test int main(int argc, char *argv[]){std::string input, output;//input = "此电摩VID_20160531_194403.mp4 一出,http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%20%E5%AD%97%E7%AC%A6%20%26&rsv_pq=cd1686ec0020b7eb&rsv_t=21a17dKwt642CgDFL8JxM%2Ft1ywlP4OECwVMil5Ij%2FbCWJdOc6UXRr%2BMaDr4&rqlang=cn&rsv_enter=1&rsv_sug3=27&rsv_sug1=20&rsv_sug7=101&rsv_sug2=0&inputT=7511&rsv_sug4=7997 谁还要汽车  http://www.arxql.zx58.cn/wanghao/1.html 玩微信的朋友可以加我微信gxys6666[em]e400905[/em]小号点这里[em]e400389[/em]二维码页面,post:_wv 1 srctype touch apptype iphone loginuin 120340009 plateform mobileqq url http%253A%252F%252Fqm.qq.com%252Fcgi-bin%252Fqm%252Fqr%253Fk%253D3t-ZOf2mUGLPAheKhi2l_c5KmisysqWH src_uin 120340009 src_scene 311 cli_scene getDetailzuzu气垫BB,只涂了半边脸,提亮肤色,特别保湿,遮盖力超级好,不挂粉,一整天都不脱妆  快来围观我的精彩微视频! http://xiaoying.tv/v/e3qd9/2/?fromApp XiaoYing toApp qzone(通过#小影#创作)";//std::cout << input << std::endl << std::endl;int test_times = 100000;//压力测试次数struct timeval ts, te;//开始时间和结束时间if(argc != 2){std::cout << "usage: ./a.out input_file" << std::endl;return 1;}std::fstream in(argv[1]);regex_t reg;if(!init(reg)){std::cerr << "init reg error" << std::endl;return 1;}gettimeofday(&ts, NULL);while(getline(in, input)){for(int i = 0; i < test_times; i++){url_filter(reg, input, output);}}gettimeofday(&te, NULL);destroy(reg);std::cout << "total time used : " << (1000000 * (te.tv_sec - ts.tv_sec) + te.tv_usec - ts.tv_usec)/1000 << " ms" << std::endl;std::cout << "total test_times : " << test_times << std::endl;//std::cout << output << std::endl << std::endl;return 0;}


1 0