7

私はstd::regex_iteratorログファイルを解析するために使用してきました。私のプログラムは数週間非常にうまく機能しており、今日までログファイルに対して実行してスタックオーバーフローが発生するまで、何百万ものログ行を解析してきました。ログファイルの1行だけが問題の原因であることが判明しました。なぜ私の正規表現がそのような大規模な再帰を引き起こしているのか知っている人はいますか?これが問題を示す小さな自己完結型プログラムです(私のコンパイラは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-7C57BEA0E081\n"
                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_ID\n"
                L"  Operation: 3\n"
                L"  Hive: HKEY_CURRENT_USER\n"
                L"  Key: Software\\Hummingbird\\PowerDOCS\\Core\\Plugins\\Fusion\\Settings\\DetailColumns\\LONEDOCS1\\Search Unsaved\\$AUTHOR.FULL_NAME;DOCSADM.PEOPLE.SYSTEM_ID\n"
                L"  ValueName: SortOrder\n"
                L"  ValueType: REG_DWORD\n"
                L"  ValueData: 0\n"
                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;
}
4

2 に答える 2

4

すべてのキャラクターでテストされるネガティブな先読みパターンは、私には悪い考えのように思えます。あなたがやろうとしていることは複雑ではありません。(1)行の残りの部分を一致させ、次に(2)L \ d以外のもので始まる次の(3)行をいくつでも一致させます(小さなバグ。以下を参照):(別の編集:これらは正規表現です。それらを文字列リテラルとして記述したい場合は、に変更する必要があります\\\

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

Ecmascriptモードでは、\ nと一致しないはずですが、その式.の2つのをいつでも次のように置き換えることができます。.[^\n]

追加のために編集:ログエントリの終わりの直前に空白行がある場合、これは機能しない可能性があることを認識していますが、これはその場合をカバーするはずです。精度を上げるためにに変更.しました:[^\n]

 [^\n]*\n(?:(?:(?:[^L\n]|L\D)[^\n]*)?\n)*
于 2012-10-10T22:10:32.807 に答える
1

正規表現は問題ないようです。少なくとも、壊滅的なバックトラックを引き起こす可能性のあるものは何もありません。

スタックの使用を減らして、正規表現を最適化する小さな可能性があります。

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正規表現であり、先読みアサーションをサポートしていないと思われます。

于 2012-10-10T21:48:48.153 に答える