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)*
    的效率稍低,但它是如何导致的?假设
    不匹配换行符,只有一种方法匹配字符串。回溯将是线性的(在成功或失败的匹配上)-它必须返回