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 Mem,2G Swap内存,Intel(R) Xeon(R) CPU E5606 @ 2.13GHz 单处理器,CentOS Linux release 6.2 (Final)操作系统。
测试样本为包含中文、英文、标点符号、特殊符号的一篇短文,共有26行,5742个字符,3个网站URL,1个文件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(®, pattern, cflags)) != 0) //编译正则表达式
{
std::cerr << "pattern compile error" << std::endl;
char err_buff[1024];
regerror(status, ®, err_buff, 1024);
std::cout << "error message:" << err_buff << std::endl;
return false;
}
return true;
}
bool destroy(regex_t & reg)
{
regfree(®);
}
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(®, 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;
}