导致堆栈溢出的正则表达式
Regular Expression causing Stack Overflow
继我之前的问题:ECMAScript Regex用于多行字符串之后,我实现了以下加载过程:
void Load( const std::string& szFileName )
{
static const std::regex regexObject( "=== ([^=]+) ===\n((?:.|\n)*)\n=== END \1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );
static const std::regex regexData( "<([^>]+)>:([^<]*)\n", std::regex_constants::ECMAScript | std::regex_constants::optimize );
std::ifstream inFile( szFileName );
inFile.exceptions( std::ifstream::badbit );
std::string szFileData( (std::istreambuf_iterator<char>(inFile)), (std::istreambuf_iterator<char>()) );
inFile.close();
std::vector<std::future<void>> vecFutures;
for( std::sregex_iterator itObject( szFileData.cbegin(), szFileData.cend(), regexObject ), end; itObject != end; ++itObject )
{
if( (*itObject)[1] == "OBJECT1" )
{
vecFutures.emplace_back( std::async( []( std::string szDataString ) {
for( std::sregex_iterator itData( szDataString.cbegin(), szDataString.cend(), regexData ) { // Do Stuff }
}, (*itObject)[2].str() ) );
}
else if( (*itObject)[1] == "OBJECT2" )
{
vecFutures.emplace_back( std::async( []( std::string szDataString ) {
for( std::sregex_iterator itData( szDataString.cbegin(), szDataString.cend(), regexData ) { // Do Stuff }
}, (*itObject)[2].str() ) );
}
}
for( auto& future : vecFutures )
{
future.get();
}
}
但是,使用此文件加载会导致堆栈溢出(参数:0x00000001、0x00332FE4):
=== OBJECT2 ===
<Name>:Test Manufacturer
<Supplier>:Test Supplier
<Address>:Test Multiline
Contact
Address
<Email>:test@test.co.uk
<Telephone Number>:0123456789
=== END OBJECT2 ===
=== OBJECT1 ===
<Number>:1
<Name>:Test
<Location>:Here
<Manufacturer>:
<Model Number>:12345
<Serial Number>:54321
<Owner>:Me
<IP Address>:0.0.0.0
=== END OBJECT1 ===
我一直找不到堆栈溢出的源,但看起来是外部std::sregex_iterator
循环造成的。
提前感谢!
下面是另一个尝试:
=== ([^=]+) ===n((?:(?!===)[^n]+n)+)=== END 1 ===
在您的C++中,它显然会写为:
=== ([^=]+) ===\n((?:(?!===)[^\n]+\n)+)=== END \1 ===
它是为最小化回溯(至少在匹配时)而设计的,尽管我现在有点累脸先生,所以可能错过了很多改进它的方法。
它做出了两个假设,用于避免大量回溯(正如其他人所说,这可能会导致堆栈溢出):
- 除了开始/结束标记线之外,在一行的开头永远不会有
===
- C++支持这些regex特性,特别是使用负前瞻(
?!
)。考虑到它是ECMAScript方言,它应该是
解释:
=== ([^=]+) ===n
匹配并捕获对象开始标记。[^=]
是避免相对少量回溯的一种方法,与您的方法相同-我们没有使用[^ ]
,因为我不知道OBJECT id中是否有空格。
((?:
开始捕获数据的组。在里面,是一个非捕获组,因为我们将单独匹配每一行。
(?!===)
负面展望-我们不希望===
出现在捕获线路的起点。
[^n]+n
单独匹配一行。
)+)
在开始和结束标记之间至少匹配一行,然后捕获单个组中的所有行。
=== END 1 ===
匹配结束标记。
比较(使用RegexBuddy):
原始版本:
- 第一场比赛:1277步
- 匹配失败:1步(这是由于对象之间的换行)
- 第二场比赛:396步
每个添加的对象都会导致前一个对象的步数增加。例如,再添加一个对象(对象2的副本,重命名为3)将导致:2203个步骤、1322个步骤、425个步骤。
此版本:
- 第一场比赛:67步
- 匹配失败:1步(再次由于对象之间的换行)
- 第二场比赛:72步
- 匹配失败:1步
- 第三场比赛:67步
神圣的灾难性回溯。罪魁祸首是(?:.|\n)*
。每当你看到这样的结构时,你就知道你在找麻烦。
为什么?因为您告诉引擎匹配任何字符(换行符除外)或换行符,尽可能多次,或者不匹配。让我带你看一遍。
引擎将按预期启动,并在没有任何重大问题的情况下匹配=== OBJECT2 ===
部分,将消耗一行换行符,然后地狱将开始。引擎消耗所有东西,一直到=== END OBJECT1 ===
,并从那里回溯到合适的匹配。回溯基本上意味着返回一步,再次应用正则表达式,看看它是否有效。基本上尝试所有可能的字符串排列。在您的情况下,这将导致数十万次尝试。这可能就是为什么这些东西对你来说是有问题的。
我不知道你的代码是否更好,或者是否有任何错误,但(?:.|\n)*
与用*s*单行修饰符(点与换行符匹配)或[Ss]*
编写.*
相同。如果用我推荐的两个结构中的一个替换该结构,您将有望不再看到堆栈溢出错误。
编辑:看看其他的解决方案,除了解释为什么它如此糟糕之外,我真的没有时间深入研究并为你的问题提供一个坚实的解决方案。
您的表达式似乎导致了大量回溯。我会把你的表情改成:
第一个:^===s+(.*?)s+===[rn]+^(.*?)[rn]+^===s+ENDs+1s+===
- 现场示例:http://www.rubular.com/r/Iydp7AAxAz
第二:^<([^>]+)>:([^<]*)
- 现场示例:http://www.rubular.com/r/mdeyMKIPcf
这两个表达式都使用以下选项:Multiline和DotMatchesAll选项。通过包括线锚^
的开始,它将回溯限制为最多一条线或一个组。
尝试使用此模式:
static const std::regex regexObject( "=== (\S+) ===\n((?:[^\n]+|\n(?!=== END \1 ===))*)\n=== END \1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );
- 使用正则表达式regex_search在字符串中查找字符串
- 在 C++ 中使用正则表达式错误时出现问题 括号表达式中的范围无效
- C++正则表达式无限循环
- FindPackageHandleStandardArgs.cmake:137 的 CMake 错误(消息):找不到 Boost (缺少:正则表达式)(找到合适的版本"1.72.0",
- 定义有趣的宏和正则表达式在Z3 C++绑定
- 带有多个字符分隔符的正则表达式
- C++ 使用增强正则表达式库时断言崩溃
- 有人可以帮助我处理正则表达式吗?
- 使用正则表达式获取大括号块的列表
- 正则表达式以匹配数字的重复模式,后跟任何类型的分隔符?
- 组合正则表达式部分使用 | 不起作用的 C++
- 为什么C++正则表达式这么慢?
- 如何使HTML5电子邮件验证正则表达式在C++中工作?
- 在C++中实现正则表达式
- C++正则表达式替换整个单词
- 用C++编写正则表达式的正确方法是什么?
- 如何从Stroustrup的C++书中解释这个正则表达式?
- 为什么这个正则表达式C++在括号表达式中抛出无效范围异常?
- C++:匹配正则表达式,什么是匹配?
- 导致堆栈溢出的正则表达式