2012-10-10 11 views
7

He estado usando std::regex_iterator para analizar archivos de registro. Mi programa ha funcionado bastante bien durante algunas semanas y ha analizado millones de líneas de registro, hasta hoy, cuando hoy lo ejecuté contra un archivo de registro y recibí un desbordamiento de la pila. Resultó que solo una línea de registro en el archivo de registro estaba causando el problema. ¿Alguien sabe por qué mi expresión regular está causando una recursión tan masiva? Aquí hay un pequeño programa autónomo que muestra el problema (mi compilador es VC2012):¿Por qué std :: regex_iterator causa un desbordamiento de pila con estos datos?

#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

El modelo parte compleja parece estar causando. No sé por qué sin embargo. –

+0

Apuesto a que está bien en Perl, aún no confío en 'std :: regex'. – Benj

+2

@Benj Wut? FUD. Puede ser una expresión regular exponencialmente mala. La mayoría de las veces se trata de estrellas kleene anidadas. Intente utilizar coincidencias no codiciosas y/o usando '+' en vez de '*' donde sea posible. También tenga cuidado con los opcionales en grupos repetidos. El mejor consejo ... Comience pequeño. Construya paso a paso. Pon a prueba tu expresión regular en cada paso. – sehe

Respuesta

4

Los patrones de búsqueda negativa que se prueban en cada personaje me parecen una mala idea, y lo que intenta hacer no es complicado. Desea hacer coincidir (1) el resto de la línea y luego (2) cualquier cantidad de líneas siguientes (3) que comiencen con algo distinto a L \ d (error pequeño, ver a continuación): (otra edición: estas son expresiones regulares; Si desea escribir como literales de cadena, es necesario cambiar \ a \\.)

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

en el modo EcmaScript, . no debe coincidir con \ n, pero siempre se puede sustituir a los dos . s en esa expresión con [^\n]

Editado para agregar: me doy cuenta de que esto puede no funcionar si hay una línea en blanco justo antes del final de la entrada del registro, pero esto debería cubrir ese caso; He cambiado a .[^\n] de precisión adicional:

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

Bien hecho ;-) Esto funciona, no se me ocurrió que esto podría hacerse sin una anticipación negativa. – Benj

+0

Vale la pena señalar para la posteridad, tuve que usar '[^ \ n]' como sugirió. – Benj

+0

@Benj Eso es bueno saber; No tengo VC dando vueltas para probarlo. Supongo que por el hecho de que usa '[^]' para significar "cualquier carácter" que [^ L] coincidirá realmente con una línea vacía también. En caso de que esto suceda, estoy editando con una pequeña modificación. – rici

1

Parece que la expresión regular está bien; al menos no hay nada en él que pueda causar retrocesos catastróficos.

Veo una pequeña posibilidad de optimizar la expresión regular, reduciendo el uso de 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")*)"      // 
    ); 

¿Te ha set the ECMAScript option? De lo contrario, sospecho que la biblioteca de expresiones regulares está por defecto en expresiones regulares POSIX, y esas no son compatibles con las afirmaciones de búsqueda anticipada.

+0

Tristemente 'std :: regex' no tiene concepto de una expresión regular multilínea (a diferencia de perl). Por lo tanto' .' no se puede usar en líneas y '^' y '$' significa el inicio/final de la línea. Estos anclajes en realidad cambian en perl dependiendo de si está en modo de línea única/múltiple. – Benj

+0

@Benj: Ah, bien, entonces eso es bueno para esta expresión regular. Supongo mi versión todavía causa un StackOverflow entonces? –

+0

Podría quedarme ciego :-) Pero, ¿qué has cambiado? ¿No es eso regex lo mismo? – Benj

Cuestiones relacionadas