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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/woshiwanghao_hi/article/details/51713012

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;
}


展开阅读全文

没有更多推荐了,返回首页