2012-08-12 2 views
1

욕심이없는 부정적인 일치를하려고하는데, 그것을 캡처해야합니다. 파이썬에서 이러한 플래그를 사용하고 있습니다. re.LOCALE | re.MULTILINE, 각 필드가 백 슬래시로 새 행에서 시작하는 일부 텍스트 파일 '데이터베이스'의 다중 행 정리를 수행합니다. 각 레코드는 \ lx 필드로 시작합니다.파이썬 정규 표현식에서 부정적 일치 캡처하기

\lx foo 
\ps n 
\nt note 1 
\ps v 
\nt note 
\ge happy 
\nt note 2 
\ge lonely 
\nt note 3 
\ge lonely 
\dt 19/Dec/2011 

\lx bar 
... 

각 \ ge 필드의 레코드 안의 어딘가에 \ ps 필드가 하나씩 있는지 확인하려고합니다. 현재 하나의 \ ps 뒤에 여러 개의 \ ge이 붙어 있기 때문에 위의 두 외로운 \ ge와 같이 복사해야합니다.

여기에 필요한 로직의 대부분은 \ ps 필드 이후이지만 다른 \ ps 또는 \ lx와 충돌하기 전에 \ ge을 찾은 다음 다른 \ ge을 찾으십시오. \ ps 필드가 두 번째 \ ge 바로 전에 복사 될 수 있도록 모든 것을 캡처하십시오.

내 비 기능적인 시도는 다음과 같습니다. 이 교체 :이와

^(\\ps\b.*?\n)((?!^\\(ps|lx)*?)^(\\ge.*?\n)((?!^\\ps)*?)^(\\ge.*?\n) 

을 : 나는 심지어 작은 파일 (34 개 라인 긴)에 메모리 오류를 받고 있어요

\1\2\3\4\1\5 

. 물론, 이것이 효과가 있었다고해도, 여러 번 실행해야 할 것입니다. 단지 두 번째 \ ge을 다루는 것이고 세 번째 또는 네 번째 것은 처리하지 않기 때문입니다. 그 점에서 어떤 아이디어라도 나에게 흥미가있을 것입니다.

업데이트 : 약간의 조정이 필요한 경우도 있지만, Alan Moore의 솔루션은 훌륭했습니다. 슬프게도, DOTALL을 꺼야했다. 그렇지 않다면, 첫 번째. * 추후 \ ps 필드를 포함하여 - 욕심이 아닌. *을 포함하여 -을 막을 수 없기 때문이다. 형태. 하지만 (? s) 수식어에 관해서는 이제 정규식 점 정보에서 배울 수있어서 기뻤습니다. 이것은 내가 일반적으로 DOTALL을 끄지 만 여전히 다른 정규 표현식에서 사용하는 것을 허용했다 이 필수적이다. 일,하지만 위의 예를 수정할 때, 그것은 "주 2"위의 \ 추신을 삽입

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps.*\n)(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)(?P<GE_BLOCK>(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n) 

: 여기

내가 필요로하는 한 줄 형식으로 아래로 응축 제안 된 정규식입니다. 또한 \ lxs와 \ ge2를 \ lx와 \ ge (몇 개가 필요 \ b)로 취급하고 있습니다.

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps\b.*\n)(?:(?!\\(?:ps|lx|ge)\b).*\n)*\\ge\b.*\n)(?P<AFTER_GE1>(?:(?!\\(?:ps|lx|ge)\b).*\n)*)(?P<GE2_LINE>\\ge\b.*\n) 

를이 대체 문자열 : 그래서, 약간 불통 버전으로 갔다 다시

\g<PS_BLOCK>\g<AFTER_GE1>\g<PS_LINE>\g<GE2_LINE> 

감사합니다!

+0

당신이하려는 것은 정규 언어 (* "어딘가에 위의 *"와 같은)에서는 불가능합니다. 단순히 파서 또는 뭔가를 작성하고 올바른 출력을 즉시 작성해야합니다. – poke

+1

정규식은 잘못된 도구라고 생각합니다. – MRAB

+0

나는 그것이 한계를 밀고있다는데 동의하지만 내 이유에 대해 thebjorn의 대답에 대한 나의 대답을 보아라. –

답변

1

DOTALL 플래그를 사용했기 때문에 메모리 오류가 발생했습니다. 귀하의 데이터가 실제로 당신이 그것을 보여준 방식으로 포맷되어 있다면, 당신은 그 깃발 어쨌든 필요하지 않습니다; 기본 동작은 원하는 것입니다. 비 탐욕적 수정 자 (?)도 필요하지 않습니다.

이 정규식을 시도해보십시오

prog = re.compile(r""" 
    ^
    (?P<PS_BLOCK> 
     (?P<PS_LINE>\\ps.*\n) 
     (?:     # Zero or more lines that 
     (?!\\(?:ps|lx|ge)) # don't start with 
     .*\n    # '\ps', '\lx', or '\ge'... 
    )* 
     \\ge.*\n    # ...followed by a '\ge' line. 
    ) 
    (?P<GE_BLOCK> 
     (?:     # Again, zero or more lines 
     (?!\\(?:ps|lx|ge)) # that don't start with 
     .*\n    # '\ps', '\lx', or '\ge'... 
    )* 
     \\ge.*\n    # ...followed by a '\ge' line. 
    ) 
    """, re.MULTILINE | re.VERBOSE) 

대체 문자열은 다음과 같습니다

r'\g<PS_BLOCK>\g<PS_LINE>\g<GE_BLOCK>' 

당신은 여전히 ​​여러 패스를해야 할 것이다. 파이썬이 \G을 지원하는 경우에는 필요하지 않지만 subn을 사용하고 number_of_subs_made 반환 값을 확인할 수 있습니다.

+0

감사합니다. 작은 조정만으로도 저에게는 큰 도움이되었습니다. 위의 질문에 업데이트를 추가했습니다. 귀하의 자세한 정규식, BTW,하지만 불행히도 내 텍스트 파일의 형식으로 인해 한 줄로 응축했다 문서화 싶습니다. –

1

때마다 당신이 regexen에 문제가 자신에게있어 "나는 여러 번 실행해야 할 것입니다 .."당신이 언어는 것

:-) 파서를 작성해야 명확한 기호입니다 꽤 정기적으로 할 수 있습니다, 그래서 AA 파서는 아마도처럼 쉬운 일부터 시작 쓸 쉽게해야합니다 :

def parse_line(line): 
    kind, value = line.split(' ', 1) # split on the first space 
    kind = kind[1:]     # remove the \ 
    parsed_value = globals().get('parse_' + kind, lambda x:x)(value) 
    return (kind, parsed_value) 

def parse_dt(value): 
    val = ... # create datetime.date() from "19/Dec/2011" 
    return val 

그것은 그것의 톤을 어쩌면 너무 상태 머신을 작성하는 globals()를 사용하여 귀여운,하지만 저장 있어요 상용구 코드 ...예를 들어, - ("ge", ..) 튜플 후 꽤 쉽게해야합니다 전에 항상 ("ps", ..) 튜플이있는 경우

records = [parse_line(line) for line in open("myfile.dta")] 

가 알아내는 : :-)

는 튜플의 목록으로 입력 변환 먼저 lx 튜플이 어디에 있는지 주목하여 ...

+0

외 : Looks slick. 여기에 정규식을 사용하고자하는 이유는 (비록 코드는 더 쉽지만) 나는 정렬 된 정규식 집합을 텍스트 파일에 저장하고 있기 때문입니다. 따라서 사용자가 실행할 수 있고 사용자 정의 할 수있는 스크립트를 제공합니다. 설명 및 각 정규식을 사용/사용하지 않는 간단한 방법으로 정규식을 알지 못하는 사이에 사용자 정의 할 수 있습니다. (평범한 텍스트에서는 이스케이프 할 문자가 필요하지 않습니다.) 내가 작성한 다른 정규 표현식은 훨씬 더 합리적입니다. 몇 가지 사용합니다. * multiline; 그러므로 DOTALL. –