为什么 std::regex_iterator 会导致此数据的堆栈溢出

Why does std::regex_iterator cause a stack overflow with this data?

本文关键字:数据 堆栈 栈溢出 regex std iterator 为什么      更新时间:2023-10-16

我一直在使用std::regex_iterator来解析日志文件。 我的程序已经运行了几个星期,并且已经解析了数百万个日志行,直到今天,今天我针对日志文件运行它并出现堆栈溢出。 事实证明,日志文件中只有一个日志行导致了问题。 有谁知道为什么我的正则表达式会导致如此大规模的递归? 这是一个小型的独立程序,它显示了问题(我的编译器是VC2012):

#include <string>
#include <regex>
#include <iostream>
using namespace std;
std::wstring test = L"L3  T15356 79726859 [CreateRegistryAction] Creating REGISTRY Action:n"
                L"  Identity: 272A4FE2-A7EE-49B7-ABAF-7C57BEA0E081n"
                L"  Description: Set Registry Value: "SortOrder" in Key HKEY_CURRENT_USER\Software\Hummingbird\PowerDOCS\Core\Plugins\Fusion\Settings\DetailColumns\LONEDOCS1\Search Unsaved\$AUTHOR.FULL_NAME;DOCSADM.PEOPLE.SYSTEM_IDn"
                L"  Operation: 3n"
                L"  Hive: HKEY_CURRENT_USERn"
                L"  Key: Software\Hummingbird\PowerDOCS\Core\Plugins\Fusion\Settings\DetailColumns\LONEDOCS1\Search Unsaved\$AUTHOR.FULL_NAME;DOCSADM.PEOPLE.SYSTEM_IDn"
                L"  ValueName: SortOrdern"
                L"  ValueType: REG_DWORDn"
                L"  ValueData: 0n"
                L"L4  T15356 79726859 [CEMRegistryValueAction::ClearRevertData] [ENTER]n";
int wmain(int argc, wchar_t* argv[])
{
    static wregex rgx_log_lines(
        L"^L(\d+)\s+"             // Level
        L"T(\d+)\s+"              // TID
        L"(\d+)\s+"               // Timestamp
        L"\[((?:\w|\:)+)\]"     // Function name
        L"((?:"                     // Complex pattern
          L"(?!"                    // Stop matching when...
            L"^L\d"                // New log statement at the beginning of a line
          L")"                      
          L"[^]"                    // Matching all until then
        L")*)"                      // 
        );
    try
    {
        for (std::wsregex_iterator it(test.begin(), test.end(), rgx_log_lines), end; it != end; ++it)
        {
            wcout << (*it)[1] << endl;
            wcout << (*it)[2] << endl;
            wcout << (*it)[3] << endl;
            wcout << (*it)[4] << endl;
            wcout << (*it)[5] << endl;
        }
    }
    catch (std::exception& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}
在每个

角色上测试的负面展望模式对我来说似乎是一个坏主意,而且您要做的事情并不复杂。你想要匹配 (1) 行的其余部分,然后 (2) 任意数量的以下 (3) 行,这些行以 L\d 以外的内容开头(小错误;见下文):(另一个编辑:这些是正则表达式;如果你想将它们写成字符串文字,你需要更改为 \

 .*n(?:(?:[^L]|LD).*n)*
 |   |  |
 +-1 |  +---------------3
     +---------------------2

在 Ecmascript 模式下,. 不应匹配 ,但您始终可以将该表达式中的两个.替换为 [^n]

编辑以添加:我意识到如果在日志条目结束之前有一个空行,这可能不起作用,但这应该涵盖这种情况;为了提高精度,我将.更改为[^n]

 [^n]*n(?:(?:(?:[^Ln]|LD)[^n]*)?n)*

正则表达式似乎没问题;至少其中没有任何可能导致灾难性回溯的内容。

我看到了优化正则表达式的小可能性,减少了堆栈的使用:

static wregex rgx_log_lines(
    L"^L(\d+)\s+"             // Level
    L"T(\d+)\s+"              // TID
    L"(\d+)\s+"               // Timestamp
    L"\[([\w:]+)\]"          // Function name
    L"((?:"                     // Complex pattern
      L"(?!"                    // Stop matching when...
        L"^L\d"                // New log statement at the beginning of a line
      L")"                      
      L"[^]"                    // Matching all until then
    L")*)"                      // 
    );

你是否设置了 ECMAScript 选项?否则,我怀疑正则表达式库默认为 POSIX 正则表达式,并且这些不支持前瞻断言。