2012-10-10 11 views
7

Sto usando std::regex_iterator per analizzare i file di registro. Il mio programma ha funzionato abbastanza bene per alcune settimane e ha analizzato milioni di righe di log, fino a oggi, quando oggi l'ho eseguito su un file di log e ottenuto un overflow dello stack. Si è scoperto che solo una riga di registro nel file di registro causava il problema. Qualcuno sa perché la mia espressione regolare sta causando una così grande ricorsione? Ecco un programma contenuto piccola auto che mostra il problema (il mio compilatore è VC2012):Perché std :: regex_iterator causa uno stack overflow con questi dati?

#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; 
} 
+0

La parte complesso schema sembra essere la causa di esso. Non so perché però. –

+0

Scommetto che va bene in perl, non mi fido abbastanza di 'std :: regex' ancora. – Benj

+2

@ Ben Wut? FUD. Può essere un'espressione regolare in modo esponenziale. Molto spesso si tratta di stelle kleene annidate. Prova a usare corrispondenze non avide e o usando '+' invece di '*' dove possibile. Fai attenzione anche agli optionals in gruppi ripetuti. Il miglior consiglio ... Inizia in piccolo. Costruisci passo dopo passo. Metti alla prova la tua espressione regolare ogni fase. – sehe

risposta

4

I modelli di lookahead negativi testati su ogni personaggio mi sembrano una pessima idea e quello che stai cercando di fare non è complicato. Si desidera abbinare (1) il resto della linea e quindi (2) qualsiasi numero di righe successive (3) che iniziano con qualcosa di diverso da L \ d (piccolo bug, vedi sotto): (un'altra modifica: queste sono espressioni regolari; se si desidera scrivere loro come stringhe letterali, è necessario modificare \ a \\.)

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

in modalità ECMAScript, . non dovrebbe corrispondere \ n, ma si può sempre sostituire le due . s in quell'espressione con [^\n]

Modificato per aggiungere: mi rendo conto che questo potrebbe non funzionare se c'è una riga vuota poco prima della fine della voce di registro, ma questo dovrebbe coprire tale caso; Ho cambiato .-[^\n] per maggiore precisione:

[^\n]*\n(?:(?:(?:[^L\n]|L\D)[^\n]*)?\n)* 
+0

Ben fatto ;-) Funziona, non mi è venuto in mente che questo potrebbe essere fatto senza un lookahead negativo. – Benj

+0

Vale la pena segnalare ai posteri, ho bisogno di usare '[^ \ n]' come suggerito. – Benj

+0

@ Benj È bello sapere; Non ho VC in giro per provarlo. Presumo dal fatto che tu usi '[^]' per significare "qualsiasi carattere" che [^ L] effettivamente corrisponda anche a una linea vuota. In questo caso, sto modificando con una piccola modifica. – rici

1

La regex sembra essere OK; almeno non c'è nulla che possa causare un ritorno indietro catastrofico.

vedo una piccola possibilità per ottimizzare l'espressione regolare, riducendo l'uso pila:

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")*)"      // 
    ); 

hai fatto set the ECMAScript option? Altrimenti, sospetto che la libreria regex abbia come valore predefinito le regex POSIX, e quelle non supportano le asserzioni lookahead.

+0

Purtroppo 'std :: regex' non ha concetto di regex multilinea (a differenza di perl), quindi' .' non può essere usato tra le righe e '^' e '$' indica l'inizio/la fine della riga.Queste ancore cambiano effettivamente in perl a seconda che tu sia in modalità single/multi line – Benj

+0

@Benj: Ah, OK, bene, allora è buono per questa regex. la mia versione causa ancora StackOverflow? –

+0

Potrei diventare cieco :-) Ma cosa hai cambiato? Non è che regex lo stesso? – Benj

Problemi correlati