C++ 导致堆栈溢出的正则表达式
除了我前面的问题:,我已经实施了以下加载过程:C++ 导致堆栈溢出的正则表达式,c++,regex,c++11,stack-overflow,standard-library,C++,Regex,C++11,Stack Overflow,Standard Library,除了我前面的问题:,我已经实施了以下加载过程: 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 co
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();
}
}
void加载(const std::string&szFileName)
{
静态常量std::regex regexObject(“==([^=]+)===\\n((?:.| \\\\n)*)\\n===END\\1==”,std::regex_常量::ECMAScript | std::regex_常量::优化);
静态常量std::regex regexData(“]+)>:([^尝试使用此模式:
static const std::regex regexObject( "=== (\\S+) ===\\n((?:[^\\n]+|\\n(?!=== END \\1 ===))*)\\n=== END \\1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );
您的表达式似乎导致了很多回溯。我将您的表达式更改为:
第一个:^==\s+(.*?)\s+==[\r\n]+^(.*?[\r\n]+^==\s+END\s+\1\s+==
- 实例:
第二:^]+)>:([^回溯。罪魁祸首是(?:.|\\n)*
。每当你看到这样的构造,你就知道你在自找麻烦
为什么?因为你告诉引擎匹配任何字符(新行除外)或新行,尽可能多地匹配,或者不匹配。让我带你看看
引擎将按预期启动并匹配====OBJECT2====
-部分,没有任何重大问题,换行符将被消耗,然后地狱将开始。引擎将消耗所有内容,一直到==END OBJECT1====
,并从那里回溯到合适的匹配。回溯基本上意味着返回一个第二步,再次应用正则表达式,看看它是否有效。基本上,用你的字符串尝试所有可能的排列。在你的情况下,这将导致几十万次尝试。这可能就是为什么东西对你来说是有问题的
我不知道您的代码是否更好或是否有任何错误,但(?:.|\\n)*
与使用*s*单行修饰符(点匹配新行)或[\s\s]编写*
是一样的*
。如果您使用我推荐的两种构造之一替换该构造,您将有望不再看到堆栈溢出错误
编辑:
也看看其他的解决方案,除了解释问题如此糟糕的原因之外,我还没有时间深入研究并为您的问题提供一个可靠的解决方案。下面是另一个尝试:
=== ([^=]+) ===\n((?:(?!===)[^\n]+\n)+)=== END \1 ===
在你的C++中,它显然会被写成:
=== ([^=]+) ===\\n((?:(?!===)[^\\n]+\\n)+)=== END \\1 ===
这是为了最小化回溯(至少在匹配时是这样),虽然我现在有点累了,所以可能错过了很多改进的方法
它做出了两个假设,用于避免大量回溯(正如其他人所说,这可能导致堆栈溢出):
除了开始/结束标记行之外,在行的开头从来没有==
C++支持这些正则表达式特征——特别是使用否定的前瞻(<代码>?<代码> >),应该考虑到ECMAScript方言。
解释:
=== ([^=]+) ===\n
匹配并捕获对象开始标记。[^=]
是避免相对少量回溯的一种方法,与您的方法相同-我们不使用[^]
,因为我不知道对象id中是否有空格
((?:
开始捕获组中的数据。在其中,是一个非捕获组,因为我们将分别匹配每一行
(?!===)
)+)
消极前瞻-我们不希望捕获行的开头出现==
[^\n]+\n
分别匹配一行
(?!===)
)+)
在开始标记和结束标记之间至少匹配一行,然后捕获单个组中的所有行
=== END \1 ===
匹配结束标记
比较(使用RegexBuddy):
=== ([^=]+) ===\n
原文:
- 第一场比赛:1277步
- 匹配失败:1步(这是由于对象之间的换行)
- 第二场比赛:396步
每个添加的对象都会导致前一个对象的步骤数量增加。例如,再添加一个对象(对象2的副本,重命名为3)将导致:2203个步骤、1322个步骤、425个步骤
此版本:
- 第一场比赛:67步
- 匹配失败:1步(再次由于对象之间的换行)
- 第二场比赛:72步
- 匹配失败:1步
- 第三场比赛:67步
编译器:MSVC 2012 Update 3,OS:Windows 7 x64一些类似的问题:当应用于上述文件时,这似乎会导致一个非终止循环。@Shaktal:而这个版本呢?不,不幸的是,它仍然会导致一个无限运行时循环。这两个正则表达式导致数据不匹配(即外环立即终止)。我已经用实例更新了答案,说明了表达式是如何工作的。我怀疑问题不在于正则表达式,而在于您的代码中的某个地方。即使是一个简单的示例也无法匹配;我将提供一个实例,但不幸的是,ideone使用的GCC目前没有提供有效的
实现。+1回溯解释。但是,您是说用<代码> > [S\S] *< /C> >(或<代码> >代码> >如果VC++有“点匹配新行”——标准C++不符合我的知识?*
据我所知,它的回溯量是相同的,只是没有那么灾难性,因为正则表达式对每个回溯都要做的步骤更少。但我很可能是错的。:-)@JimmiTh:对于我提供的解决方案,仍然会有很多回溯,但是没有接近原始帖子数量的地方。这将是完全可管理的,而且还可以。回溯将是不同的,因为引擎只需要通过[\S\S]减少匹配
并且不考虑替换。在这种情况下,延迟匹配会使引擎回溯更少。我不确定我是否相信这种解释。(?:。|\\n)*
的效率稍低,但它是如何导致的?假设
不匹配换行符,只有一种方法匹配字符串。回溯将是线性的(在成功或失败的匹配上)-它必须返回