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

원자 그룹 — (?>...)

~10 min · atomic, 백트래킹, performance

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

한 번 확정한 매칭으로 돌아가지 않는 그룹

원자 그룹 (?>...)은 안쪽 패턴을 매칭한 뒤 그 그룹 내부로 다시 백트래킹하지 않아. 뒤의 패턴이 실패해도 이미 소비한 문자를 돌려주거나 그룹 안의 다른 대안을 다시 시도하지 않는다는 뜻이야.

백트래킹 경우의 수를 줄일 수 있어

^([a-z]+)*X는 끝에 X가 없는 긴 소문자 문자열에서 반복 구간을 나누는 여러 방법을 시도할 수 있어. 중첩 수량자가 만드는 모호한 분할은 파국적 백트래킹의 대표적인 원인이야.

^(?>[a-z]+)*X처럼 안쪽을 원자 그룹으로 만들면 이미 잡은 소문자 구간을 다시 나누지 않아. 다만 원자 그룹은 매칭 결과도 바꿀 수 있으므로, 뒤의 패턴을 위해 문자를 돌려줄 필요가 없다는 사실을 확인한 뒤 사용해야 해.

엔진마다 지원 여부가 달라

  • Python re: Python 3.11부터 지원해. 이전 버전의 내장 re에서는 사용할 수 없어.
  • PCRE, Perl, Java, Ruby, .NET: 지원해.
  • JavaScript: 원자 그룹 문법을 지원하지 않아.
  • RE2와 Go regexp: 백트래킹 엔진이 아니므로 이 문법을 지원하지 않아.

사용하기 전에 의미를 확인해

원자 그룹은 단순한 속도 표지가 아니라 백트래킹 선택지를 없애는 문법이야. 일반 그룹과 원자 그룹을 각각 성공해야 하는 입력, 실패해야 하는 입력, 긴 입력에 테스트하고 의미가 그대로인지 확인해.

Code

원자 그룹과 일반 그룹·python
import re
import sys

# 중첩 수량자가 있는 모호한 패턴
unsafe = re.compile(r'^([a-z]+)*X$')

# Python 3.11 이상에서 원자 그룹으로 내부 분할을 확정
if sys.version_info >= (3, 11):
    atomic = re.compile(r'^(?>[a-z]+)*X$')
    bool(atomic.fullmatch('abcX'))  # True
    bool(atomic.fullmatch('abc'))   # False

# 원자 그룹은 결과도 바꿀 수 있어
greedy = re.fullmatch(r'a+a', 'aa')
if sys.version_info >= (3, 11):
    committed = re.fullmatch(r'(?>a+)a', 'aa')
    bool(greedy)     # True — a+가 문자 하나를 돌려줌
    bool(committed)  # False — 원자 그룹은 돌려주지 않음

External links

Exercise

Python 3.11 이상에서 re.fullmatch(r'(\w+)+X', 'a' * 30)re.fullmatch(r'(?>\w+)+X', 'a' * 30)의 시간을 안전한 제한 안에서 비교해. 전체 일치로 시작 위치를 고정해도 안쪽 반복 분할 때문에 차이가 생기는 이유를 설명해.

Progress

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

댓글 0

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

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