2012-10-10 35 views
7

Tôi đã sử dụng std::regex_iterator để phân tích các tệp nhật ký. Chương trình của tôi đã hoạt động khá tốt trong vài tuần và đã phân tích cú pháp hàng triệu dòng đăng nhập, cho đến hôm nay, khi hôm nay tôi chạy nó đối với tệp nhật ký và bị tràn ngăn xếp. Hóa ra rằng chỉ một dòng đăng nhập trong tệp nhật ký đã gây ra sự cố. Có ai biết tại sao regex của tôi đang gây ra sự đệ quy khổng lồ như vậy? Đây là một chương trình tự chứa nhỏ hiển thị vấn đề (trình biên dịch của tôi là VC2012):Tại sao std :: regex_iterator gây ra tràn ngăn xếp với dữ liệu này?

#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

Phần mẫu phức tạp dường như đang gây ra. Không biết tại sao mặc dù. –

+0

Tôi đặt cược nó là tốt trong perl, tôi không hoàn toàn tin tưởng 'std :: regex' được nêu ra. – Benj

+2

@Benj Wut? FUD. Nó có thể là một regex sai theo cấp số nhân. Thông thường nó là về các ngôi sao lồng nhau. Hãy thử sử dụng các kết quả không tham lam và hoặc sử dụng '+' thay vì '*' nếu có thể. Cũng xem ra với các tùy chọn trong các nhóm lặp đi lặp lại. Lời khuyên tốt nhất ... Bắt đầu nhỏ. Xây dựng từng bước. Kiểm tra regex của bạn từng bước. – sehe

Trả lời

4

Mẫu tra cứu tiêu cực được kiểm tra trên mọi nhân vật dường như là một ý tưởng tồi với tôi và những gì bạn đang cố gắng làm không phức tạp. Bạn muốn kết hợp (1) phần còn lại của dòng và sau đó (2) bất kỳ số nào sau (3) dòng bắt đầu bằng một cái gì đó khác với L \ d (lỗi nhỏ; xem bên dưới): (chỉnh sửa khác: đây là các regex; nếu bạn muốn viết chúng như xâu, bạn cần thay đổi \ để \\.)

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

trong chế độ ECMAScript, . không phải phù hợp \ n, nhưng bạn luôn có thể thay thế hai . s ở chỗ biểu hiện với [^\n]

Đã chỉnh sửa để thêm: Tôi nhận thấy rằng điều này có thể không hoạt động nếu có dòng trống ngay trước khi kết thúc mục nhật ký, nhưng điều này sẽ bao gồm trường hợp đó; Tôi đã thay đổi . thành [^\n] để có độ chính xác cao hơn:

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

Làm tốt lắm ;-) Công việc này, nó không xảy ra với tôi rằng điều này có thể được thực hiện mà không có một lookahead tiêu cực. – Benj

+0

Đó là giá trị chỉ ra cho hậu thế, tôi đã cần phải sử dụng '[^ \ n]' như bạn đề nghị. – Benj

+0

@Benj Thật tuyệt khi biết; Tôi không có VC đá xung quanh để thử nó với. Tôi đoán từ thực tế là bạn sử dụng '[^]' để có nghĩa là "bất kỳ nhân vật nào" mà [^ L] sẽ thực sự phù hợp với một dòng trống. Trong trường hợp điều này xảy ra, tôi đang chỉnh sửa với một sửa đổi nhỏ. – rici

1

Regex có vẻ OK; ít nhất là không có gì trong đó có thể gây ra thảm họa ngược.

tôi nhìn thấy một khả năng nhỏ để tối ưu hóa các regex, cắt giảm trên stack sử dụng:

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

Bạn có set the ECMAScript option? Nếu không, tôi nghi ngờ các thư viện regex mặc định để POSIX regexes, và những người không hỗ trợ xác nhận lookahead.

+0

Đáng buồn 'std :: regex' không có khái niệm về một regex đa cấp (không giống như perl). Vì vậy,' .' không thể được sử dụng trên các dòng và '^' và '$' có nghĩa là bắt đầu/kết thúc của dòng.Các neo thực sự thay đổi trong perl tùy thuộc vào việc bạn đang ở chế độ đơn/đa dòng. – Benj

+0

@Benj: Ah, OK, vậy thì tốt cho regex này. Phiên bản của tôi vẫn gây ra một StackOverflow sau đó? –

+0

Tôi có thể bị mù :-) Nhưng bạn đã thay đổi điều gì? Không phải là regex như vậy? – Benj

Các vấn đề liên quan