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

게으른 (비탐욕적) 매칭 — *? +? ??

~10 min · lazy, non-greedy, engine

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

수량자 뒤에 ?를 붙이면 가능한 적게 잡아

  • *?는 0번 이상 가운데 가장 적은 횟수부터 시도하고
  • +?는 1번 이상 가운데 가장 적은 횟수부터 시도하고
  • ??는 0번을 먼저 시도하고
  • {n,m}?n번부터 시도해.

게으른 수량자도 나머지 패턴이 실패하면 소비량을 늘리며 백트래킹해. 탐욕적 수량자와 출발 방향이 반대일 뿐, 같은 백트래킹 엔진 위에서 움직이는 거야.

가까운 닫는 표식에서 멈추고 싶을 때 유용해

.*는 여러 <p> 블록을 마지막 </p>까지 한꺼번에 잡을 수 있어. .*?는 나머지 패턴이 처음 성공하는 닫는 표식에서 멈추므로 각 블록을 나누어 찾기 쉬워져.

게으르다고 항상 더 빠른 건 아니야

적게 잡은 뒤 늘리는 편과 많이 잡은 뒤 줄이는 편 가운데 어느 쪽이 빠른지는 입력과 나머지 패턴에 달려 있어. 그래서 게으른 수량자의 주된 선택 기준은 성능이 아니라 원하는 경계에서 멈추는지야.

경계가 한 글자라면 부정형 문자 클래스도 살펴봐

<p>([^<]*)</p>는 다음 < 전까지의 문자를 캡처해. .*?보다 경계가 분명하고 모호한 선택지도 적어. 그래도 수량화된 문자 클래스는 뒤쪽 패턴이 실패하면 문자를 돌려줄 수 있으니, 부분 표현만 보고 백트래킹이 없다고 단정하면 안 돼.

Code

게으른 수량자가 탐욕적 재앙 수정·python
import re

html = '<p>first</p> <p>second</p>'

# 탐욕적 — 의도보다 많이 먹음
re.findall(r'<p>.*</p>', html)
# ['<p>first</p> <p>second</p>']

# 게으른 — 각 블록에서 멈춤
re.findall(r'<p>.*?</p>', html)
# ['<p>first</p>', '<p>second</p>']

# 더 명확한 경계 — 부정형 문자 클래스
# 뒤 패턴이 실패하면 이 수량자도 백트래킹할 수 있음
re.findall(r'<p>[^<]*</p>', html)
# ['<p>first</p>', '<p>second</p>']

# 따옴표 문자열 — 같은 원리
re.findall(r'"[^"]*"', '"a" "b" "c"')
# ['"a"', '"b"', '"c"']  — 경계가 분명함. 패턴 전체의 안전성은 따로 확인해야 함

External links

Exercise

<p>first</p> <p>second</p>에서 각 문단을 따로 추출하도록 게으른 수량자, 부정형 문자 클래스, 전방 탐색 <p>(.*?)(?=</p>)을 사용한 패턴을 각각 만들어. 결과가 같은지 확인한 뒤 100KB짜리 통제된 HTML 예시에서 실행 시간을 비교해.

Progress

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

댓글 0

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

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