본문 바로가기
C.W.K.
Stream
Lesson 03 of 12 · published

재귀 패턴 — (?R)

~10 min · recursive, advanced, pcre

Level 0패턴 호기심
0 XP0/90 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

패턴 안에서 패턴을 다시 호출하기

일부 엔진은 패턴 전체나 특정 그룹을 재귀적으로 다시 호출할 수 있어. 이 기능을 사용하면 깊이가 정해지지 않은 괄호처럼 중첩된 구조도 매칭할 수 있지만, 일반적인 정규 표현식보다 강한 엔진별 확장이야.

대표 문법

  • (?R) — 패턴 전체를 재귀 호출
  • (?N) — 번호가 N인 그룹을 재귀 호출
  • (?P>name) 또는 (?&name) — 이름 붙은 그룹을 호출

정확한 문법과 동작은 엔진마다 다르므로 배포할 엔진의 문서를 확인해야 해.

균형 잡힌 괄호 예시

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

여는 괄호 뒤에서 괄호가 아닌 문자나 재귀적으로 매칭한 괄호 쌍을 반복하고 마지막 닫는 괄호를 잡아. (?R)이 패턴 전체를 다시 호출하므로 중첩 깊이를 미리 정하지 않아도 돼.

엔진 지원은 제한적이야

  • PCRE2와 Perl: 재귀와 서브루틴 호출을 지원해.
  • Python 외부 regex 모듈: 지원해.
  • Ruby: 서브표현식 호출 문법으로 재귀 구조를 표현할 수 있어.
  • Python 내장 re, JavaScript, .NET, Java, Go: 이 재귀 문법을 지원하지 않아.

재귀는 이식성이 낮아. 사용하면 특정 엔진에 종속되므로 입력 형식과 배포 환경을 함께 기록해.

먼저 전용 파서를 검토해

재귀 정규식이 필요하다면 이미 구조 파싱 문제일 가능성이 커. Python 코드는 ast, JSON은 json, HTML은 HTML 파서처럼 형식을 이해하는 도구가 오류 처리와 이스케이프 규칙까지 맡아 줘. 제한된 추출에 재귀 정규식을 쓰더라도 완전한 파서를 대신한다고 가정하지 마.

Code

재귀 패턴 (PCRE / Python regex)·text
# PHP와 PCRE에서
preg_match('/\((?:[^()]++|(?R))*\)/', 'foo(bar(baz)qux)quux', $m)
# $m[0] = '(bar(baz)qux)'

# Python 외부 regex 모듈에서
import regex
m = regex.search(r'\((?:[^()]++|(?R))*\)', 'foo(bar(baz)qux)quux')
print(m.group())  # '(bar(baz)qux)'

# Python 내장 re는 재귀 문법을 지원하지 않아

# 이름 붙은 그룹을 재귀 호출
pattern = r'(?P<paren>\((?:[^()]++|(?P>paren))*\))'
m = regex.search(pattern, 'foo(bar(baz))')
print(m.group('paren'))

External links

Exercise

PHP, PCRE를 쓰는 grep, Python regex 모듈 가운데 하나에서 중첩 중괄호를 재귀 패턴으로 매칭해 봐. 같은 입력을 json.loads로 처리하고, 이스케이프된 따옴표·중첩 객체·잘못된 문법을 어느 쪽이 더 정확히 다루는지 비교해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.