首先,感谢 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>>。一旦第十二个字段匹配失败,引擎回溯到原子组前面的<<^>>。
正则表达式没写好,效率简直差别太大了。
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位。
那么用正则的话,如果你通过规避前面自己总结的一些坑,还是没有避免的话,就需要用一些外部的库了。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.