이것은 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"입니다. n
는 normal
일치하고 s
이 special
일치하는 구조
nnnsnnsnnnnsnnsnn
을 가지고 아무것도에 맞게 사용됩니다.
이 특별한 경우에는 재귀를 사용하기 때문에 상황이 좀 더 복잡합니다. (?P>parenthesized)
은 반복적으로 parenthesized
패턴을 사용합니다 (이 패턴의 일부 임). 엔진은 그룹 ...
이 일치하는 것을 시도하지 않지만 대신 서브 패턴을 다시 적용한다는 점을 제외하고는 역 참조와 같은 비트로 (?P>...)
구문을 볼 수 있습니다.
내 패턴은 올바른 괄호 패턴에 대한 빈 문자열을 제공하지 않지만 실패합니다. 당신은 lookbehind를 떠나서 이것을 고칠 수 있습니다. lookbehind는 실제로 필요하지 않습니다. 엔진이 항상 맨 왼쪽 일치를 리턴하기 때문입니다.
편집 : 귀하의 예를 두 가지에 의해 판단은, 당신은 실제로 마지막 타의 추종을 불허하는 괄호 후 모든,하지만 첫 번째 쉼표까지에만 모든 것을 원하지 않는다. 내 결과를 사용하여 ,
에서 분할하거나 보헤미안의 답을 시도 할 수 있습니다.
추가 읽기 :
(라는 이름의 그룹을 포함)
편집 : 나는 당신이 당신의 질문에 당신이 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
이 두 짧은 루프 것 다른 사람들이 미래에 이해하기가 훨씬 쉬우 며 정규 표현식 솔루션 (재귀가없는 적어도 하나 이상)보다 훨씬 융통성이 있습니다.
+1 우수 테스트 케이스 – Bohemian