【问题标题】:Stack overflow caused by backtracking in regular expression正则表达式回溯导致的堆栈溢出
【发布时间】:2015-04-09 08:27:51
【问题描述】:

我想使用以下正则表达式将字符串与使用 std::regex 的 C++11 匹配:

([^;]|'[^']*')*

我想要实现的是:我想匹配除分号之外的所有字符,但是,如果用撇号括起来,分号应该被忽略(这表示一个字符串)。尝试应用正则表达式时出现堆栈溢出。我意识到问题是由excessive backtracking 引起的。不幸的是,我不知道如何摆脱这个问题。如何重写表达式以使其不会导致大量回溯?

带有虚拟文本的最小代码示例:

#include <regex>
#include <string>
int main()
{
    std::string str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut suscipit enim. Praesent varius purus ac sem vulputate pulvinar. Mauris scelerisque arcu tortor, at bibendum dui rhoncus ut. Nunc dictum malesuada condimentum. Mauris ornare nunc eget efficitur tempor. Morbi ex nibh, consectetur vitae bibendum id, bibendum varius purus. Proin finibus quam vel ornare molestie. Mauris condimentum nisi efficitur, fringilla massa ut, commodo diam. Mauris lobortis laoreet magna sed commodo. Donec faucibus lectus placerat ex pulvinar interdum.";
    std::regex rgx("([^;]|'[^']*')*");
    std::regex_match(std::begin(str), std::end(str), rgx); // Stack overflow!
    return 0;
}

我正在使用 Visual Studio 2012。

【问题讨论】:

  • 不太确定问题出在哪里,但您可以尝试不捕获(?:[^;]|'[^']*')*,并稍微调整正则表达式(?:''|'[^']+'|[^;])*
  • @YOU:我已经尝试过非捕获分组,没有成功。你建议的小调整也没有用。
  • +而不是*怎么样?它改变了正则表达式,并且不会匹配空白字符串,但你可以试试。
  • @YOU 还是会导致栈溢出。
  • 你可以试试RE2

标签: c++ regex c++11 stack-overflow backtracking


【解决方案1】:

如果您将正则表达式语法切换为基本 posix 而不是默认的 ECMAscript,它将不再溢出:

std::regex rgx("([^;]|'[^']*')*", std::regex_constants::basic);

我用 MSVC2013 进行了测试,它可以工作。不幸的是,它也不符合您的期望,正如这个小变体所示:

...
std::smatch cm;
std::regex_search(str, cm, rgx, std::regex_constants::match_any | std::regex_constants::match_continuous | std::regex_constants::match_not_null); // No overflow!
for (int i = 0; i < cm.size(); i++)
    std::cout << "Found:" << cm[i];  // but no result is printed out. 

如果选择std::regex_constants::extended 选项,堆栈溢出又回来了。

如果你用 RegExr 上的数据测试你的正则表达式,你就会明白问题所在:

正则表达式会导致无限的潜在匹配。一旦你减少了潜在的匹配集,例如在Lorem 之后放置一个;,就不再有堆栈溢出。

这确实很不幸:似乎没有标准匹配选项(例如std::regex_constants::match_any | std::regex_constants::match_continuous | std::regex_constants::match_not_null)可以解决这个常见问题(例如:没有“最大匹配”)。

尽管如此,它仍然与标准库的 MSVC 实现相关。这个online GCC example 使用相同的表达式运行良好。因此,boostRE2 替代方案可能值得考虑。

此代码在 MSVC2013 上使用 boost 1.57,运行没有任何问题。如您所见,它使用与标准相同的代码/名称,但将std 替换为boost

#include <iostream>
#include <boost/regex.hpp>  // instead of <regex>
#include <string>
                            // use the boost alternative to std
using namespace boost::regex_constants;  
using boost::regex; using boost::smatch; using boost::regex_search;

int main()
{
    std::string str = "your very long string..." ;
    regex rgx("([^;]|'[^']*')*", extended | nosubs);
    smatch cm;
    //regex_match(str, cm, rgx, regex_constants:: match_continuous | match_not_null); // Stack overflow!
    regex_search(str, cm, rgx, match_continuous | match_not_null); 
    for (int i = 0; i < cm.size(); i++)
        std::cout << "Found:" << cm[i];
    return 0;
}

【讨论】:

  • @djf 在刷新屏幕之前我看不到您的帖子。然而,我同时意识到改变选项是不够的,VS 的实现确实存在问题。
  • 根据语法definition of basic regex,您在测试时没有使用正确的语法。据我了解,必须转义 BRE 中的括号。这样做时,我遇到了类似的堆栈溢出问题。
  • @sven 完全正确:实际上它接受正则表达式(没有引发异常),但没有匹配项,如上面的代码所示。所以你必须使用extended或默认语法,MSVC实现会挂起,除非有很短的匹配。这确实是一个 MSVC 问题,因为我将代码复制/粘贴到 ideone 以查看它使用 GCC 成功运行。
猜你喜欢
  • 2013-07-05
  • 2013-08-09
  • 2013-03-19
  • 2013-03-17
  • 2016-12-17
  • 1970-01-01
  • 2017-06-23
  • 1970-01-01
  • 2016-10-07
相关资源
最近更新 更多