이번 포스팅에서는 탐욕 수량자(Greedy Match)와 게으른 수량자(Lazy, Non-greedy Match)에 대한 개념을 소개하고 파이썬(Python)을 통한 예제를 알아보자. 이때 수량자에 대한 내용을 모르는 분들은 여기를 참고하면 좋다.
탐욕 수량자(Greedy Match)와 게으른 수량자(Lazy, Non-greedy Match)
1) 탐욕 수량자(Greedy Match)
탐욕 수량자는 패턴 매칭이 중간에서 멈추지 않고 마지막까지 찾는 수량자를 말한다. 적당히를 모르고 욕심이 많아서 찾을 수 있는 곳까지 찾는다 하여 탐욕 수량자라고 하는 것 같다. 일반적으로 사용하는 수량자는 모두 탐욕 수량자이다.
예를 들어 'aacefasdfc'라는 문자열을 a.*c라는 패턴을 찾는다고 해보자. 온라인 정규 표현식 테스트 사이트인 regex101.com에 들어가서 테스트해보면 'aac'에서 멈춰도 되지만 맨 마지막에 'c'가 있어서 결국 전체 문자열인 'aacefasdfc'을 찾게 되는 것이다.
2) 게으른 수량자(Lazy, Non-greedy Match)
게으른 수량자는 패턴 매칭 작업이 더 이루어질 수 있음에도 중간에 패턴을 발견하는 즉시 매칭 작업이 종료되는 수량자를 말한다. 일(패턴 매칭 작업)을 더 할 수 있음에도 중간에 끝마치기 때문에 게으른 수량자라고 불리는 것 같다. 게으른 수량자는 수량자 뒤에 물음표 '?'을 추가하면 된다.
예를 들어 'aacefsdfc'라는 문자열을 a.*?c라는 패턴을 찾는다고 해보자. 앞에서 살펴보았던 탐욕 수량자와는 달리 전체 문자열을 매칭할 수 있음에도 불구하고 중간에 'aac'를 매칭하고 작업을 종료하게 된다.
3) 파이썬 예제
a. 기본
아래 코드는 탐욕 수량자와 게으른 수량자의 예시를 나타낸 것이다. 코드를 실행해보면서 차이점을 알 수 있다.
(1) .* vs .*?
string = 'accefcc'
print(re.match(r'a.*c', string).group())
# accefcc
print(re.match(r'a.*?c', string).group())
# ac
(2) .+ vs .+?
string = 'accefcc'
print(re.match(r'a.+c', string).group())
# accefcc
print(re.match(r'a.+?c', string).group())
# acc
(3) .? vs .??
string = 'accefcc'
print(re.match(r'a.?c', string).group()) ## a와 c 사이에 최대 문자 하나까지 찾으므로 acc가 매칭
# acc
print(re.match(r'a.??c', string).group()) ## a와 c 사이에 문자가 없어도 되므로 ac가 매칭
# ac
(4) {n, } vs {n, }?
string = 'accddee'
print(re.match(r'a.{2,}e', string).group())
# accddee
print(re.match(r'a.{2,}?e', string).group())
# accdde
(5) .+ vs .+?
string = 'accddeessee'
print(re.match(r'a.{2,8}e', string).group()) ## a와 e 사이에 최대 8개까지 문자가 있는지 확인
# accddeesse
print(re.match(r'a.{2,8}?e', string).group()) ## a와 e 사이에 2~8개수 범위 내에 문자열 발견 즉시 종료
# accdde
b. 응용
이번엔 좀 더 실용적인 예제를 알아보자. html에서 특정 태그를 찾고 싶을 때, 예를 들어 문자열 '<h2>Hello<h2>'에서 <h2>를 매칭하고 싶을 때 게으른 수량자를 활용할 수 있다. 이때 탐욕 수량자를 사용하면 어떻게 되는지도 함께 확인해보자.
string = '<h2>Hello<h2>'
print(re.match(r'<.+>', string).group())
# <h2>Hello<h2>
print(re.match(r'<.+?>', string).group())
# <h2>
'프로그래밍 > 정규표현식' 카테고리의 다른 글
[정규 표현식] 응용 (2) 숫자인지 아닌지 확인하기 with Python (0) | 2022.10.13 |
---|---|
[정규 표현식] 응용 (1) URI(URL)에서 정보 가져오기 with Python (2) | 2022.10.11 |
[정규 표현식] re 모듈 알아보기 (3) finditer 사용법과 findall과의 차이 (0) | 2022.10.01 |
[정규 표현식] 역 참조(Back reference) 알아보기 (0) | 2022.10.01 |
[정규 표현식] re 모듈 알아보기 (2) 대체하기 (feat. re.sub) (0) | 2022.09.30 |
댓글