C ++ - 在长多行匹配期间,STD正则表达式在MSVC中崩溃

时间:2014-12-06 11:14:50

标签: c++ regex std

我正在尝试使用std :: regex从源文件中提取/* ... */样式注释。但是“regex_search”有时会在跨越多行的长匹配中崩溃(未处理的异常)。

STD示例(不工作)

这个例子让我崩溃了:

#include <iostream>
#include <regex>

int main()
{
    std::string in = "/*\naaa\naaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaa\naaaaaaaaa\n*/";
    std::regex e(".*/\\*(\n|.)*?\\*/");
    std::smatch m;

    while (std::regex_search(in, m, e))
    {
        std::cout << m[0].str() << std::endl;

        in = m.suffix();
    }

    return 0;
}

我正在使用Visual Studio 2013,因此这可能是编译器特定的问题。

修改:正如@ T.C.在评论中指出,代码在GCC 4.9下工作,它会引发堆栈溢出异常。它可能只是Visual C ++编译器的问题,或者只是GCC分配更大的堆栈。

Qt示例(工作)

我尝试在Qt中实现同样的事情没有问题,所以我认为我没有犯任何错误。但我非常希望不依赖任何外部库。

QRegularExpression re(".*/\\*(\n|.)*?\\*/");
QRegularExpressionMatchIterator it = re.globalMatch(QString(in.c_str()));
while (it.hasNext())
{
    QRegularExpressionMatch match = it.next();
    QString word = match.captured(0);
}

问题

这可能是std :: regex实现中的错误吗?我在哪里犯了错误?

2 个答案:

答案 0 :(得分:2)

我认为这不是编译器问题(如果你不使用gcc&lt; 4.9)。正则表达式崩溃是因为获得结果的步骤量太高。尝试对此模式执行相同的操作:

/\\*[\\s\\S]*?\\*/

或使用此模拟possessive quantifier

/\\*(?=((?:[^*]+|\\*(?!/))*))\\1\\*/

(这两种模式设计用于ECMAScript模式,也就是说,如果我没有错,则是正则表达式引擎的默认模式)

关于您的原始模式:

第一个错误是使用.* 开始您的模式(因为您使用regex_search方法而不需要)。由于量词默认为贪心,因此第一个子模式将匹配所有字符,直到每行结束。在获得匹配之后,正则表达式引擎需要逐个字符地回溯,直到它在字符串中找到/*(请注意,如果在同一行中有多个/*,则只有最后一个找到了)

第二个错误是使用(\\n|.)*?之类的内容来描述所有字符,直到后面的内容(即*/)。

使用这种结构有几个成本:

  • 您使用捕获组,因此您需要支付每个字符的存储成本(逐个)。
  • 您支付了更改的费用,因为大部分时间.都会匹配而\\n无法检测(但是,这取决于您的评论的样子,但是写{ {1}}可能会更高效。)
  • 最重要的成本可能是您使用具有非贪婪量词的组的事实,因为它强制所有字符的正则表达式引擎进入组并为每个字符保留组。 如果没有延迟量词,在某些正则表达式引擎中(?:.|\\n)*?可能比(?:a)+ 慢150倍

关于你在评论中提出的问题,我会给你一个总答案。

是的,步骤或回溯步骤的数量在某处受到限制。如果正则表达式引擎足够智能,它可能会在预分析期间检测到模式在尝试执行某些操作之前会导致过多的工作,但情况并非总是如此。

要确切知道发生了什么,可以将你的正则表达式模式放在try / catch块中,并检查这两个错误:

a+

答案 1 :(得分:1)

嘿。我的静态分析代码最近遇到了同样的问题。所以这是解决方案,虽然它依赖于第三方库(我的):

// http://www.benhanson.net/lexertl.html
#include <lexertl/generator.hpp>
#include <lexertl/iterator.hpp>

int main()
{
    std::string in = "/*\naaa\naaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaa\naaaaaaaaa\naaaaaaaaa\naaaaaaaaa\n*/";
    lexertl::rules rules;
    lexertl::state_machine sm;

    rules.push("[/][*](\n|.)*?[*][/]", 1);
    rules.push(".|\n", rules.skip());
    lexertl::generator::build(rules, sm);

    lexertl::citerator iter(in.c_str(), in.c_str() + in.size(), sm);
    lexertl::citerator end;

    for (; iter != end; ++iter)
    {
        std::cout << iter->str() << std::endl;
    }

    return 0;
}