2013-06-12 4 views
4

RegEx에 대해 약간의 지식이 있지만 현재로서는 내 능력보다 훨씬 위에 있습니다.RegEx : 마지막 괄호 바로 뒤의 텍스트

일치하는 닫는 괄호가없는 마지막 괄호 바로 뒤에 텍스트/표현식을 찾으려면 도움이 필요합니다.

개발중인 오픈 소스 소프트웨어 (오브젝트 파스칼)의 CallTip입니다. 몇 가지 예 아래

:

------------------------------------ 
Text     I need 
------------------------------------ 
aaa(xxx    xxx 
aaa(xxx,    xxx 
aaa(xxx, yyy   xxx 
aaa(y=bbb(xxx)  y=bbb(xxx) 
aaa(y <- bbb(xxx)  y <- bbb(xxx) 
aaa(bbb(ccc(xxx  xxx 
aaa(bbb(x), ccc(xxx xxx 
aaa(bbb(x), ccc(x) bbb(x) 
aaa(bbb(x), ccc(x), bbb(x) 
aaa(?, bbb(??   ?? 
aaa(bbb(x), ccc(x)) '' 
aaa(x)    '' 
aaa(bbb(    '' 
------------------------------------ 

For all text above the RegEx proposed by @Bohemian 
(?<=\()(?=([^()]*\([^()]*\))*[^()]*$).*?(?=[ ,]|$)(?! <-)(?<! <-) 
matches all cases. 

For the below (I found these cases when implementing the RegEx in the software) not 
------------------------------------ 
New text    I need 
------------------------------------ 
aaa(bbb(x, y)   bbb(x, y) 
aaa(bbb(x, y, z)  bbb(x, y, z) 
------------------------------------ 

는 이러한 상황에 대한 정규식 (PCRE)를 쓸 수 있습니까? 이전 게시물 (RegEx: Word immediately before the last opened parenthesis) 앨런 무어 (많은 감사가 새로) 바로 아래의 정규식과 마지막 오픈 괄호하기 전에 텍스트를 찾을하는 데 도움이에서

그러나

\w+(?=\((?:[^()]*\([^()]*\))*[^()]*$) 

, 나는 할 수 없습니다 즉시 일치하도록 적절한 조정을하십시오.

누구든지 도와 드릴 수 있습니까?

+0

+1 우수 테스트 케이스 – Bohemian

답변

1

사용 모습 이것은 미리 :

(?<=\()(?=([^()]*\([^()]*\))*[^()]*$).*?(\(.*?\))?(?=[ ,]|$)(?! <-)(?<! <-) 

참조 this running on rubular 질문에 게시 된 모든 테스트 케이스를 전달합니다.

+0

이것은 결과를 어디에서 찾을 수 있는지에 대해서는 일관성이 없습니다. 'aaa (bbb (x), ccc (x)'에 대한 결과는 그룹 '0'에 있습니다. 즉, 'aaa (y (<- bbb (xxx) 실제 일치). 원하는 결과에 더 깊은 중첩 수준이 포함되어있는 경우에도 작동하지 않습니다. –

+0

m.buettner에 동의합니다!이 표현은 훌륭하지만 완료되지 않았습니다 ... – jcfaria

+0

@ m.buettner 이전 버전은 그 중 하나에서만 실패했습니다. 테스트 케이스. 수정을 위해 미리 추가했습니다. 편집 된 답변 및 루블 링크를 참조하십시오. – Bohemian

6

이것은 this problem과 유사합니다. 재귀 구문을 사용하여 PCRE를 사용하고 있기 때문에 실제로 솔루션이 있습니다.

/ 
(?(DEFINE)    # define a named capture for later convenience 
    (?P<parenthesized>  # define the group "parenthesized" which matches a 
          # substring which contains correctly nested 
          # parentheses (it does not have to be enclosed in 
          # parentheses though) 
    [^()]*    # match arbitrarily many non-parenthesis characters 
    (?:     # start non capturing group 
     [(]     # match a literal opening (
     (?P>parenthesized) # recursively call this "parenthesized" subpattern 
          # i.e. make sure that the contents of these literal() 
          # are also correctly parenthesized 
     [)]     # match a literal closing) 
     [^()]*    # match more non-parenthesis characters 
    )*     # repeat 
)      # end of "parenthesized" pattern 
)       # end of DEFINE sequence 

# Now the actual pattern begins 

(?<=[(])     # ensure that there is a literal (left of the start 
          # of the match 
(?P>parenthesized)?  # match correctly parenthesized substring 
$       # ensure that we've reached the end of the input 
/x      # activate free-spacing mode 

이 패턴의 요지는 분명히 parenthesized 서브 패턴입니다. 아마도 좀 더 자세히 설명해야 할 것입니다. normal[^()]special입니다

(normal* (?:special normal*)*) 

[(](?P>parenthesized)[)]이다 : 그것은 구조가 이것이다입니다. 이 기술은 "unrolling-the-loop"입니다. nnormal 일치하고 sspecial 일치하는 구조

nnnsnnsnnnnsnnsnn 

을 가지고 아무것도에 맞게 사용됩니다.

이 특별한 경우에는 재귀를 사용하기 때문에 상황이 좀 더 복잡합니다. (?P>parenthesized)은 반복적으로 parenthesized 패턴을 사용합니다 (이 패턴의 일부 임). 엔진은 그룹 ...이 일치하는 것을 시도하지 않지만 대신 서브 패턴을 다시 적용한다는 점을 제외하고는 역 참조와 같은 비트로 (?P>...) 구문을 볼 수 있습니다.

내 패턴은 올바른 괄호 패턴에 대한 빈 문자열을 제공하지 않지만 실패합니다. 당신은 lookbehind를 떠나서 이것을 고칠 수 있습니다. lookbehind는 실제로 필요하지 않습니다. 엔진이 항상 맨 왼쪽 일치를 리턴하기 때문입니다.

편집 : 귀하의 예를 두 가지에 의해 판단은, 당신은 실제로 마지막 타의 추종을 불허하는 괄호 후 모든,하지만 첫 번째 쉼표까지에만 모든 것을 원하지 않는다. 내 결과를 사용하여 ,에서 분할하거나 보헤미안의 답을 시도 할 수 있습니다.

추가 읽기 :

(라는 이름의 그룹을 포함)
  • PCRE Subpatterns
  • PCRE Recursion
  • "풀기 - 더 - 루프는"그의 책 Mastering Regular Expressions에 제프리 프리들에 의해 도입,하지만 난 링크 된 게시물을 생각했다 위의 내용은 좋은 개요를 제공합니다.
  • (?(DEFINE)...)을 사용하면 실제로 conditional patterns이라는 또 다른 기능을 악용합니다. PCRE man pages은 어떻게 작동하는지 설명합니다. "참조 용으로 만 사용할 수 있도록 하위 패턴 정의"페이지를 검색하면됩니다.

편집 : 나는 당신이 당신의 질문에 당신이 Object Pascal을 사용하고 있다고 언급 한 것을 알아 차렸다. 이 경우 실제로 PCRE를 사용하지 않을 것입니다. 즉, 재귀를 지원하지 않습니다. 이 경우 문제에 대한 완전한 정규식 솔루션이 될 수 없습니다. 예를 들어, "일치하지 않는 마지막 괄호 뒤에 하나 이상의 중첩 수준이있을 수 있습니다"와 같은 제한을 적용하면 해결 방법을 찾을 수 있습니다. 다시 말하지만, "unrolling-the-loop"를 사용하여 xxx(xxx)xxx(xxx)xxx 형태의 부분 문자열과 일치시킵니다. 당신이 xxx(yyy()) 일치하려는 당신이 이제까지 aaa(xxx(yyy()) 같은 입력 예를 추가하는 경우

(?<=[(])   # make sure we start after an opening (
(?=    # lookahead checks that the parenthesis is not matched 
    [^()]*([(][^()]*[)][^()]*)* 
       # this matches an arbitrarily long chain of parenthesized 
       # substring, but allows only one nesting level 
    $    # make sure we can reach the end of the string like this 
)    # end of lookahead 
[^(),]*([(][^()]*[)][^(),]*)* 
       # now actually match the desired part. this is the same 
       # as the lookahead, except we do not allow for commas 
       # outside of parentheses now, so that you only get the 
       # first comma-separated part 

다음이 방법은 일치하지 않습니다. 사실, 재귀를 사용하지 않는 정규 표현식은 임의의 중첩 수준을 처리 할 수 ​​없습니다.

정규 표현식은 재귀를 지원하지 않으므로 정규 표현식을 사용하지 않는 것이 좋습니다. 마지막 정규식이 현재 입력 된 모든 예제와 일치하더라도 정말 뒤죽박죽이고 문제가되지 않을 수도 있습니다. 어때? 문자열을 문자 단위로 이동하고 괄호 위치 스택을 유지합니다. 그런 다음 의사는 당신에게 마지막으로 타의 추종을 불허하는 ( 후 모든 것을 제공합니다

while you can read another character from the string 
    if that character is "(", push the current position onto the stack 
    if that character is ")", pop a position from the stack 
# you've reached the end of the string now 
if the stack is empty, there is no match 
else the top of the stack is the position of the last unmatched parenthesis; 
    take a substring from there to the end of the string 

다음 첫 번째 중첩되지 쉼표까지 모든 것을 얻으려면을, 다시 그 결과를 걸을 수 :

nestingLevel = 0 
while you can read another character from the string 
    if that character is "," and nestingLevel == 0, stop 
    if that character is "(" increment nestingLevel 
    if that character is ")" decrement nestingLevel 
take a substring from the beginning of the string to the position at which 
    you left the loop 

이 두 짧은 루프 것 다른 사람들이 미래에 이해하기가 훨씬 쉬우 며 정규 표현식 솔루션 (재귀가없는 적어도 하나 이상)보다 훨씬 융통성이 있습니다.

+0

답장을 보내 주셔서 감사합니다. 그러나 RegEx에 대한 지식은 약간 복잡합니다. 정규식을 만들고 테스트하기 위해 무료 도구 (** Expresso ** 및 ** EditPadPro **)를 사용합니다. 두 사람 모두 의견과 다중 라인이있는 RegEx를 이해하지 못합니다. 제발, 당신은 한 줄로 쓸 수 있어요? – jcfaria

+0

@jcfaria 주석과 요소 사이의 공백을 모두 제거하면됩니다.'(? (DEFINE) (? P <괄호로 묶음> [^()] * (? : [(] 괄호로 묶음) []] [ $?) (괄호 안의)? $ ** –

+0

하지만이 도구는이 RegEx에서 오류를 보였다. (괄호로 묶은)? ** ** 불법 그룹 구문, 0 또는 하나 반복 ** – jcfaria