2012-10-10 17 views
7

मैं लॉग फ़ाइलों को पार्स करने के लिए std::regex_iterator का उपयोग कर रहा हूं। मेरा कार्यक्रम कुछ हफ्तों के लिए काफी अच्छी तरह से काम कर रहा है और आज तक लाखों लॉग लाइनों को पार्स कर चुका है, जब आज मैंने इसे लॉग फ़ाइल के खिलाफ चलाया और एक स्टैक ओवरफ्लो प्राप्त किया। यह पता चला कि लॉग फ़ाइल में केवल एक लॉग लाइन समस्या पैदा कर रही थी। क्या किसी को पता है कि मेरा रेगेक्स इतनी भारी रिकर्सन क्यों कर रहा है? यहां एक छोटा सा निहित कार्यक्रम है जो इस मुद्दे को दिखाता है (मेरा कंपाइलर वीसी2012 है):std :: regex_iterator इस डेटा के साथ एक स्टैक ओवरफ़्लो क्यों करता है?

#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

जटिल पैटर्न हिस्सा यह कारण हो रहा है। हालांकि क्यों नहीं पता। –

+0

मुझे लगता है कि यह पर्ल में ठीक है, मुझे अभी तक 'std :: regex' पर भरोसा नहीं है। – Benj

+2

@ बेंज वट? FUD। यह एक घातीय रूप से misbehaving regex हो सकता है। अक्सर यह घोंसला वाले क्लेन सितारों के बारे में है। गैर-लालची मैचों का उपयोग करने का प्रयास करें और जहां संभव हो '' 'के बजाय' + 'का उपयोग करें। बार-बार समूहों में विकल्प के साथ भी देखें। सबसे अच्छी सलाह ... छोटे से शुरू करें। चरण-दर-चरण बनाएं। प्रत्येक चरण में अपने regex का परीक्षण करें। – sehe

उत्तर

4

प्रत्येक चरित्र पर परीक्षण किए गए नकारात्मक लुकहेड पैटर्न सिर्फ मेरे लिए एक बुरा विचार प्रतीत होता है, और आप जो करने की कोशिश कर रहे हैं वह जटिल नहीं है। आप (1) शेष रेखा से मिलान करना चाहते हैं और फिर (2) निम्नलिखित में से कोई भी संख्या (3) रेखाएं जो एल \ डी (छोटी बग; नीचे देखें) के अलावा कुछ और शुरू करती हैं: (एक और संपादन: ये regexes हैं; यदि आप उन्हें स्ट्रिंग शाब्दिक रूप में लिखना चाहते हैं, आप \\ को \ बदलने की जरूरत है।)

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

ECMAScript मोड में, . से मेल खाते हैं \ n नहीं होना चाहिए, लेकिन आप हमेशा साथ कि अभिव्यक्ति में दो . रों बदल सकते [^\n]

जोड़ने के लिए संपादित: मुझे एहसास है कि लॉग प्रविष्टि के अंत से पहले एक खाली रेखा होने पर यह काम नहीं कर सकता है, लेकिन इसमें उस मामले को शामिल करना चाहिए; मैं अतिरिक्त परिशुद्धता के लिए [^\n] को . बदल दिया है:

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

अच्छी तरह से किया ;-) यह काम करता है, यह मेरे लिए नहीं हुआ था कि यह नकारात्मक दिखने के बिना किया जा सकता है। – Benj

+0

वंशावली के लिए यह इंगित करने लायक है, जैसा कि आपने सुझाव दिया था, मुझे '[^ \ n]' का उपयोग करने की आवश्यकता थी। – Benj

+0

@ बेंज यह जानना अच्छा है; मेरे पास वीसी के साथ कोशिश करने के लिए चारों ओर लात मारना नहीं है। मैं इस तथ्य से मानता हूं कि आप '[^]' का मतलब किसी भी चरित्र "का अर्थ है [^ एल] वास्तव में एक खाली रेखा से भी मेल खाएगा। यदि ऐसा होता है, तो मैं एक छोटे से संशोधन के साथ संपादन कर रहा हूँ। – rici

1

रेगेक्स ठीक प्रतीत होता है; कम से कम इसमें कुछ भी नहीं है जो विनाशकारी बैकट्रैकिंग का कारण बन सकता है।

मैं एक छोटे से संभावना regex अनुकूलन करने के लिए, ढेर उपयोग पर नीचे काटने देखें:

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

आप set the ECMAScript option किया? अन्यथा, मुझे लगता है कि रेगेक्स लाइब्रेरी पॉज़िक्स रेगेक्स पर डिफ़ॉल्ट है, और वे लुकहेड दावे का समर्थन नहीं करते हैं।

+0

दुख की बात है कि 'std :: regex' में मल्टीलाइन रेगेक्स (पर्ल के विपरीत) की कोई अवधारणा नहीं है। इसलिए '.' का उपयोग लाइनों में नहीं किया जा सकता है और'^' और '$' का अर्थ लाइन की शुरुआत/अंत है। ये एंकर वास्तव में एकल/बहु लाइन मोड में हैं या नहीं, इस पर निर्भर करते हुए ये एंकर वास्तव में perl में बदलते हैं। – Benj

+0

@ बेंज: आह, ठीक है, ठीक है तो यह इस रेगेक्स के लिए अच्छा है। मुझे लगता है मेरा संस्करण अभी भी एक स्टैक ओवरफ्लो का कारण बनता है? –

+0

मैं अंधे जा रहा था :-) लेकिन आपने क्या बदल दिया है? क्या वह वही नहीं है? – Benj

संबंधित मुद्दे