[regex] 균형 괄호와 일치하는 정규식

두 개의 괄호 사이의 모든 텍스트를 선택하려면 정규식이 필요합니다.

예: some text(text here(possible text)text(possible text(more text)))end text

결과: (text here(possible text)text(possible text(more text)))



답변

정규식은 중첩 구조, 즉 재귀를 처리하므로 작업에 잘못된 도구입니다.

그러나이 작업을 수행하는 간단한 알고리즘 이 있습니다.이 질문 에서 이전 질문에 대한 답변 을 설명 했습니다 .


답변

빠른 참조를 위해이 답변을 추가하고 싶습니다. 자유롭게 업데이트하십시오.


밸런싱 그룹을 사용하는 .NET 정규식 .

\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)

c깊이 카운터로 사용되는 곳 .

Regexstorm.com의 데모


재귀 패턴을 사용하는 PCRE .

\((?:[^)(]+|(?R))*+\)

regex101의 데모 ; 또는 교대없이 :

\((?:[^)(]*(?R)?)*+\)

regex101의 데모 ; 또는 성능을 위해 풀었다 :

\([^)(]*+(?:(?R)[^)(]*)*+\)

regex101의 데모 ; (?R)를 나타내는 패턴이 붙여 넣어 (?0)집니다.

Perl, PHP, Notepad ++, R : perl = TRUE , Python : Perl 동작 을 위한 정규식 패키지(?V1) .


하위 표현식 호출을 사용하는 루비 .

Ruby 2.0 \g<0>을 사용하면 전체 패턴을 호출 할 수 있습니다.

\((?>[^)(]+|\g<0>)*\)

Rubular에서 데모 ; Ruby 1.9는 그룹 재귀 캡처 만 지원합니다 .

(\((?>[^)(]+|\g<1>)*\))

Rubular 데모  ( Ruby 1.9.3 이후 원자 그룹화 )


JavaScript  API :: XRegExp.matchRecursive

XRegExp.matchRecursive(str, '\\(', '\\)', 'g');

최대 2 단계의 중첩을 재귀하지 않는 JS, Java 및 기타 정규 표현식 :

\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)

regex101의 데모 . 패턴에 더 깊은 중첩이 추가 되어야합니다.
불균형 괄호에서 더 빨리 실패하려면 수량 자를 삭제하십시오 +.


자바 : @jaytea의 참조를 사용 하는 흥미로운 아이디어 .


참조-이 정규식은 무엇을 의미합니까?


답변

정규식 재귀 를 사용할 수 있습니다 .

\(([^()]|(?R))*\)


답변

[^\(]*(\(.*\))[^\)]*

[^\(]*문자열의 시작 부분에 여는 괄호가 아닌 모든 항목을 일치 (\(.*\))시키고 괄호로 묶인 필수 하위 문자열을 캡처하고 문자열 [^\)]*의 끝에서 닫는 괄호가 아닌 모든 항목을 일치시킵니다. 이 표현식은 대괄호와 일치하지 않습니다. 간단한 파서 ( dehmann의 답변 참조 )가 더 적합 할 것입니다.


답변

(?<=\().*(?=\))

일치하는 두 괄호 사이에서 텍스트를 선택하려면 정규식이 적합하지 않습니다. 불가능합니다 (*) .

이 정규식은 문자열의 첫 번째 여는 마지막 괄호 사이의 텍스트를 반환합니다.


(*) 정규식 엔진에 그룹 또는 재귀 균형 조정 과 같은 기능이없는 한 . 이러한 기능을 지원하는 엔진의 수가 점차 증가하고 있지만 여전히 일반적으로 사용 가능한 것은 아닙니다.


답변

이 답변은 왜 정규 표현식이이 작업에 적합한 도구가 아닌지에 대한 이론적 한계를 설명합니다.


정규식은 이것을 할 수 없습니다.

정규식은로 알려진 컴퓨팅 모델을 기반으로합니다 Finite State Automata (FSA). 이름에서 알 수 있듯이 FSA현재 상태 만 기억할 수 있으며 이전 상태에 대한 정보는 없습니다.

FSA

위의 다이어그램에서 S1과 S2는 S1이 시작 및 최종 단계 인 두 가지 상태입니다. 따라서 string 0110을 사용하면 전환이 다음과 같이 진행됩니다.

      0     1     1     0
-> S1 -> S2 -> S2 -> S2 ->S1

우리가 두 번째에있을 때 위의 단계에서, S2구문 분석 후 즉, 010110는 FSA는 이전에 대한 정보가 없습니다 0에서 01그것은 단지 현재 상태 및 다음 입력 기호를 기억할 수 있습니다.

위의 문제에서 우리는 여는 괄호가 없다는 것을 알아야합니다. 즉 , 어떤 곳에 보관 해야합니다 . 그러나 FSAs그렇게 할 수 없기 때문에 정규 표현식을 작성할 수 없습니다.

그러나이 작업을 수행하기 위해 알고리즘을 작성할 수 있습니다. 알고리즘은 일반적으로 아래에 해당됩니다 Pushdown Automata (PDA). PDA의 한 수준 위에 FSA있습니다. PDA에는 추가 정보를 저장하기위한 추가 스택이 있습니다. PDA는 ‘ push‘스택의 여는 괄호와 pop닫는 괄호를 만나면 ‘ ‘ 할 수 있기 때문에 위의 문제를 해결하는 데 사용할 수 있습니다 . 마지막에 스택이 비어 있으면 여는 괄호와 닫는 괄호가 일치합니다. 그렇지 않으면 아닙니다.


답변

실제로는 .NET 정규 표현식을 사용하여 수행 할 수는 있지만 사소한 것은 아니므로주의 깊게 읽으십시오.

여기서 좋은 기사를 읽을 수 있습니다 . .NET 정규식을 읽어야 할 수도 있습니다. 여기서 읽을 수 있습니다 .

꺾쇠 괄호 <>는 이스케이프가 필요하지 않기 때문에 사용되었습니다.

정규식은 다음과 같습니다.

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>