正则表达式的耗时问题+hyperscan库使用

  1. 自己对正则表达式的一些理解

首先,感谢 https://dragon.cnblogs.com/archive/2006/05/09/394923.html中的相关文章 。
该灵感来自于Aho-Corasick算法和单词查找树数据结构(Trie data structure)。--后期这个也要学习一下。
如果想学习正则表达式的话,http://www.regexlab.com/zh/regref.htm,是一个很不错的选择。 
使用RegexBuddy可以很清晰的看到匹配解析过程
正则表达式:提升效率的网站及方法:
http://www.0430.com/cn/web246371/
https://blog.csdn.net/xx_xxxxxxxxxx1120/article/details/80641319
(1)关注如何让匹配更快失败
    具体方法有:
    1.尽量让开头就匹配失败
    2.如果有次数限制,尽量给出一个范围
    3.尽量且范围如:[a-c]来代替a|b|c
    4.尽量用[^]-非来限定字符范围
    5.如果有|,最好最左端的为最可能匹配选项
    6.谨慎用点号元字符,尽可能不用星号和加号这样的任意量词
    7.能使用懒惰匹配就坚决不用贪婪匹配
    8.合理使用括号。每使用一个普通括号(),而不是非捕获型括号(?:),就会保留一部分内存等着再次访问。
    9.原子组与防止回溯:原子组的目的是使正则引擎失败的更快一点。因此可以有效的阻止海量回溯。原子组的语法是<<(?>正则表达式)>>。位于(?>)之间的所有正则表达式都会被认为是一个单一的正则符号。一旦匹配失败,引擎将会回溯到原子组前面的正则表达式部分。前面的例子用原子组可以表达成<<^(?>(.*?,){11})P>>。一旦第十二个字段匹配失败,引擎回溯到原子组前面的<<^>>。

正则表达式的耗时问题+hyperscan库使用_第1张图片

正则表达式没写好,效率简直差别太大了。
regex中与特定的语法相结合的,现在一个问题来了,C++11中默认的语法是ECMAScript ,那么难道它不支持 ?>,如果支持,效率就会有很大提升。
http://www.cplusplus.com/reference/regex/basic_regex/ 里面有一些其它的语法,我们来看下。http://www.cplusplus.com/reference/regex/basic_regex/flags/
还有一种降低耗时的就是通过 

13.  原子组与防止回溯
在一些特殊情况下,因为回溯会使得引擎的效率极其低下。
让我们看一个例子:要匹配这样的字串,字串中的每个字段间用逗号做分隔符,第12个字段由P开头。
我们容易想到这样的正则表达式<<^(.*?,){11}P>>。这个正则表达式在正常情况下工作的很好。但是在极端情况下,如果第12个字段不是由P开头,则会发生灾难性的回溯。如要搜索的字串为“1,2,3,4,5,6,7,8,9,10,11,12,13”。首先,正则表达式一直成功匹配直到第12个字符。这时,前面的正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11,”,到了下一个字符,<

>并不匹配“12”。所以引擎进行回溯,这时正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11”。继续下一次匹配过程,下一个正则符号为点号<<.>>,可以匹配下一个逗号“,”。然而<<,>>并不匹配字符“12”中的“1”。匹配失败,继续回溯。大家可以想象,这样的回溯组合是个非常大的数量。因此可能会造成引擎崩溃。 用于阻止这样巨大的回溯有几种方案: 一种简单的方案是尽可能的使匹配精确。用取反字符集代替点号。例如我们用如下正则表达式<<^([^,\r\n]*,){11}P>>,这样可以使失败回溯的次数下降到11次。 另一种方案是使用原子组。 原子组的目的是使正则引擎失败的更快一点。因此可以有效的阻止海量回溯。原子组的语法是<<(?>正则表达式)>>。位于(?>)之间的所有正则表达式都会被认为是一个单一的正则符号。一旦匹配失败,引擎将会回溯到原子组前面的正则表达式部分。前面的例子用原子组可以表达成<<^(?>(.*?,){11})P>>。一旦第十二个字段匹配失败,引擎回溯到原子组前面的<<^>>。

这样的方法,但是需要测试一下,看哪些语法支持 "^(?>(.*?,){11})P" ,这种写法,于是我做了下面的测试,代码如下:

int main()
{
    using namespace std::regex_constants;
    //std::regex first("[a-z]+");
    //std::regex second("[a-z]+", extended | icase);

    //std::regex second0("^(?>(.*?,){11})P", ECMAScript);//no
    std::regex second1("^(?>(.*?,){11})P", basic);//ok
   // std::regex second2("^(?>(.*?,){11})P", extended);//no
    //std::regex second3("^(?>(.*?,){11})P", awk);//no
    std::regex second4("^(?>(.*?,){11})P", grep);//ok
    //std::regex second5("^(?>(.*?,){11})P", egrep);

    return 0;
}

那下面就好办了,我们就用这些支持防止回溯的办法来写正则表达式。下面是测试程序:

int main()
{
    using namespace std::regex_constants;
    //std::regex first("[a-z]+");
    //std::regex second("[a-z]+", extended | icase);

    //std::regex second0("^(?>(.*?,){11})P", ECMAScript);//no
    std::regex second1("^(?>(.*?,){11})P", basic);//ok
   // std::regex second2("^(?>(.*?,){11})P", extended);//no
    //std::regex second3("^(?>(.*?,){11})P", awk);//no
   // std::regex second4("^(?>(.*?,){11})P", grep);//ok
    //std::regex second5("^(?>(.*?,){11})P", egrep);

    clock_t  nBeginTimeT = clock();
    if (std::regex_match("1,2,3,4,5,6,7,8,9,10,11,12,13", std::regex("^(.*?,){11}P")))
        std::cout << "string literal matched\n";
    else std::cout << "string literal not  matched\n";
    string sdiffTimeT = to_string((double)(clock() - nBeginTimeT));
    cout << "TEST 0000000 used:" << sdiffTimeT << "ms" << endl << endl;

    nBeginTimeT = clock();
    if (std::regex_match("1,2,3,4,5,6,7,8,9,10,11,12,13", std::regex("^([^,\r\n]*,){11}P")))
        std::cout << "string literal matched\n";
    else std::cout << "string literal not  matched\n";
    sdiffTimeT = to_string((double)(clock() - nBeginTimeT));
    cout << "TEST 1111111111  used:" << sdiffTimeT << "ms" << endl << endl;

    nBeginTimeT = clock();
    if (std::regex_match("1,2,3,4,5,6,7,8,9,10,11,12,13", std::regex("^(?>(.*?,){11})P", basic) ))
        std::cout << "string literal matched\n";
    else std::cout << "string literal not  matched\n";
    sdiffTimeT = to_string((double)(clock() - nBeginTimeT));
    cout << "TEST 22222222  used:" << sdiffTimeT << "ms" << endl << endl;
    int ab0 = 99;

    return 0;
}

下面是运行结果:

string literal not  matched
TEST 0000000 used:634.000000ms

string literal not  matched
TEST 1111111111  used:1.000000ms

string literal not  matched
TEST 22222222  used:0.000000ms

那么正则表达式的类型有哪些,有如下:

enum syntax_option_type
	{	// specify RE syntax rules
	ECMAScript = 0x01,
	basic = 0x02,
	extended = 0x04,
	awk = 0x08,
	grep = 0x10,
	egrep = 0x20,
	_Gmask = 0x3F,

	icase = 0x0100,
	nosubs = 0x0200,
	optimize = 0x0400,
	collate = 0x0800
	};

OK,那么接下来就是利用这些正则表达式来进行操作了,速度很快的哦。正则中还有其它的如boost、pcre等,后面也会做介绍。悲催了,basic对中文支持不行,出不来所以basic中的?> 还不能用。只能更改回  ECMAScript., 另外,其中对字符数目的限制,是指的是对字节的限制,如果在ANSI中,一个中文占用2位,UTF8中,一个中文占用3位。

  1. 2.来自Intel的高性能的正则表达式匹配库——Hyperscan

那么用正则的话,如果你通过规避前面自己总结的一些坑,还是没有避免的话,就需要用一些外部的库了。https://www.jianshu.com/p/0ca3e14c20e8
https://www.hyperscan.io/   是它的一些网址。

下面就是直接编译:
1.添加源码
2.把E:\NLP\localSaAnalysis\hyperscan_lak\hyperscan-master\cmake\config.h.in添加到工程中。并且更改其名字为:config.h.  添加到的路径为E:\NLP\localSaAnalysis\hyperscan_lak\lakVs2017Test\ConsoleApplication1\hyperscan_code.

 

你可能感兴趣的:(其它的已解决的问题(非程序))