2013-07-13 2 views
2

<regex> 라이브러리 (Microsoft Visual Studio 2012 : 업데이트 3)를 사용하여 응용 프로그램에 대해 약간 더 안전한로드 절차를 구현하고 몇 가지 치아가 발생했습니다 어려움 (참조 : Regular Expression causing Stack Overflow, Concurrently using std::regex, defined behaviour?ECMAScript Regex for a multilined string 참조).Microsoft의 std :: regex 구현

정규식을 사용하여 (스택 오버 플로우 등을 발생시키는) 초기 문제가 발생했습니다. here이 제안되어 잘 작동합니다. 그러나 내 파일이 너무 크면 스택 오버플로가 발생합니다 (스택 커밋 및 예약 크기를 늘림으로써이를 피할 수 있음). 또는 스택 크기가 스택 오버플로가 발생하지 않을만큼 충분히 클 경우에는 std::regex_error이 표시됩니다. 오류 코드 12 (error_stack). 여기

이 문제 복제 자체에 포함 된 예는 다음과 같습니다 (4kB의 커밋과 1메가바이트 예약) 기본 스택 크기와이 컴파일

#include <iostream> 
#include <string> 
#include <regex> 

std::string szTest = "=== TEST1 ===\n<Example1>:Test Data\n<Example2>:More Test Data\n<Example3>:Test\nMultiline\nData\n<Example4>:[email protected]\n<Example5>:\n=== END TEST1 ===\n=== TEST2 ===\n<Example1>:Test Data 2\n<Example2>:More Test Data 2\n<Example3>:Test\nMultiline\nData\n2\n<Example4>:[email protected]\n=== END TEST2 ===\n=== TEST3 ===\n<Example1>:Random Test Data\n<Example 2>:More Random Test Data\n<Example 3>:Some\nMultiline\nRandom\nStuff\n=== END TEST3 ===\n\ 
         === TEST1 ===\n<Example1>:Test Data (Second)\n<Example2>:Even More Test Data\n<Example3>:\n=== END TEST1 ==="; 

int main() 
{ 
    static const std::regex regexObject("=== ([^=]+) ===\\n((?:.|\\n)*)\\n=== END \\1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize); 

    for(std::sregex_iterator itObject(szTest.cbegin(), szTest.cend(), regexObject), end; itObject != end; ++itObject) 
    { 
     std::cout << "Type: " << (*itObject)[1].str() << std::endl; 
     std::cout << "Data: " << (*itObject)[2].str() << std::endl; 

     std::cout << "-------------------------------------" << std::endl; 
    } 
} 

스택 오버플로 예외가 발생 당할 것이다; 스택 크기 (8kB 커밋 및 2MB 예약)를 변경하면 std::regex_error에 오류 코드 12 (error_stack)가 발생합니다.

이러한 오류를 방지하기 위해 할 수있는 일이 있습니까? 아니면 정규식 라이브러리가 작은 문자열 (예 : DoB 검사 등)에서만 사용되도록 설계 되었습니까?

미리 감사드립니다.

답변

2

문제는 뒷면 참조 (\1)입니다. 역 참조는 악의적 인 것이거나 일반적으로 구현하기가 매우 어렵 기 때문에 일반적이지 않은 경우를 인식하는 것은 쉽지 않습니다. 귀하의 경우에는

이 문제는 정규 표현식의 첫 경기가 마지막=== END TEST1 ===에 대한 첫 번째 === TEST1 ===에서이 될 것입니다. 그건 당신이 의도 한 것이 아니지만 regexes가 작동하는 방식입니다. 가장 긴 맨 왼쪽 규칙. 이론적으로 스택을 죽이지 않고 정규식을 매치 할 수는 있지만, 사용중인 정규식 라이브러리가 최적화를 수행 할만큼 충분히 영리한 것인지는 의문입니다. ((?:.|\\n)*?)로 변경 :

당신은 당신이 데이터 부분 (((?:.|\\n)*)) 비 욕심함으로써 일치시킬 일치하는 정규 표현식을 수정할 수 있습니다. 그 수도 수도 스택이 폭발하기 전에 훨씬 이전에 일치하는 정규식이 발생할 수 있기 때문에 스택 폭발 문제를 해결할 수 있습니다.그러나 그것이 일반적으로 효과가 있는지 나는 모른다. 나는 MS 구현에 대해 정말로 모른다.

내 의견으로는, 코드를 조금 복잡하게 만들었다 고하더라도 역 참조는 피해야합니다.

=== ([^=]+) ===\n 

후 종료 문자열을 만들 : 내가 뭘 할 것은 첫 번째 일치하는 것입니다

"\n=== END " + match[1].str() + " ===" 

다음 find() 종료 문자열을. 즉, 불행한 regex 라이브러리의 반복자를 더 이상 사용할 수 없지만 루프는 여전히 간단합니다. 그런데

, 나는 그것이 이상한은 행의 에있는 경우에만 시작 구분 기호를 인식하는 것을 발견하고, 최종 구분 기호는 행의 시작에있는 경우. 내 성향은 그들 모두가 행이되도록 요구했을 것입니다. regex-with-back-reference를 두 단계 접근 방식으로 바꾸면 비교적 쉽게 수행 할 수 있습니다. regex-back-reference가 실제로 올바른 접근법이 아니라는 또 다른 힌트로 간주 될 수 있습니다.

+0

고마워,이 모든 문제를 해결 한 것 같습니다! +1 –

3

<regex>을 잊어 버리십시오. 적어도 현재로서는 잠재적으로 좋습니다. 제 생각에는 스펙이 깨져서 사용할 수 없습니다. 그러나 그렇지 않은 경우라도 적어도 현재의 구현은 있으며 향후 수년 동안 지속될 것입니다.

이것은 모든 주요 공급 업체가 기존의 테스트를 거친 라이브러리에 의존하지 않고 처음부터 고유 한 정규식 엔진을 구현하기 때문입니다. 이것은 엄청난 노력입니다.

내 권장 사항 : 지금 다른 regex 라이브러리를 사용하고 <regex>에 넓은 선석을 지정하십시오. 대안은 Boost.Regex, Boost.XpressivePCRE 또는 Oniguruma과 같은 (C 스타일) 라이브러리입니다.

덧붙여서 오늘 우리는 채팅에서이 주제에 관해 토론했습니다. 30 분이라면 you can read my detailed rant and some interesting counter-points.

+2

[인용 필요], [인용 필요], [인용 필요] 및 [인용 필요]. 나 또한 추천 부스트 정규식 – sehe

+0

@sehe 글쎄, 채팅을 참조하십시오. 또한, "제 견해로는"분명히 부끄럽지 만, 인용의 필요성을 배제합니다. –

+0

나는 거기에 있었고, 기억한다. 또한 "IMO"는 모든 주장에 명확하게 적용되지 않습니다. 실제로 "그러나 그것이 아니더라도"명시 적으로 그 절의 주장에 대한 그 조항의 효과를 제거하는 것으로 보인다. – sehe