이 포스팅은 Regular Expression 시리즈 12 편 중 7 번째 글 입니다.
목차
문자열 소비
정규식을 공부하다 보면 Assertion
이라는 단어를 자주 보게된다. Assertion
이라는 단어는 검사, 검증, 확인 이라는 의미를 갖고 있다. 결과적으로 어떤 것을 검사하겠다는 의미로 많이 사용한다. 그런데 Zero-width는 무엇인가? 너비가 없다는 의미인데 왜 이런 추상적인 단어를 썼는지는 아직도 모르겠다. 하지만 말을 끼워 맞춰서 연상을 쉽게 해보도록 하자.
이 개념을 알기 전에 우리가 알아야 할 것이 있다. 지금 이 글을 앞의 메타 문자와 따로 쓴 이유가 여기에 있다. 앞에서 배운 메타문자를 사용하게 되면 우리는 문자열을 소비한다. 이게 무슨 소리일까.
이 굉장히 추상적인 개념은 예시를 들어 한번만 생각하면 굉장히 쉽게 이해가 가능하다. 예를 들어 아버지가방에들어간다
라는 문자열이 있다고 해보자. 내가 궁금한 정보는 가방
을 찾고 방에
를 찾고 싶다. 이러한 문제에 대해 [가방]
이렇게 정규식을 작성하고 검색을 진행했을 때, 정규식 엔진이 어떤 방식으로 구동하는지를 따라가 보자.
아버지
부분 까지는 매치가 되지 않으므로 그냥 지나간다.가방
을 확인하고는 매치되었으므로 문자열을 소비해 버린다. 즉 없애 버린다와 같은 개념으로 보는 것이다.- 소비된 문자열은 쳐다도 안보고 그 다음 문자열인
에들어간다
라는 문자열에 대해가방
문자열을 찾는다. 가방
을 찾는 정규표현식은 끝난다.방에
를 찾는 정규표현식이 시작되고, 찾지 못한 상태로 끝난다.
이 예시를 보다보면, 소비라는 것이 어떤 의미로 사용되었는지 이해할 수 있다. 즉, 매치가 되는 경우 해당 부분이 없어지게 된다. 정말 소비라는 단어에 걸맞는 행동이다.
Zero-width Assertion
그렇다면, 너비가 없는 확인 이라는 의미의 Zero-width Assertion
은 무엇일까. 위에서 가방
이라는 단어는 2의 너비를 가진다고 볼 수 있다. 그리고 매칭이 되었을 때, 이 2의 너비에 해당하는 문자열이 소비된다. 이러한 관점에서 보았을 때, 0의 너비를 가진다고 함은, 검증을 진행하는데 있어 소비가 되는 문자열이 없다라는 의미로 와닿는다. 결과적으로 이러한 해석이 맞으며, Zero-width Assertion
는 검색을 진행하는데 있어 위에서 알아본 문자열 소비가 없는 방법을 말한다. 이제 이러한 종류의 메타 문자에 대해서 알아보자.
^ 또는 $ 문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 \^, $ 로 사용하면 된다.
메타문자 | 설명 | |
---|---|---|
또는 이라는 의미 (Or) | ||
^ | 문자열의 맨 처음과 일치하는가?(옵션에 따라 의미가 다름) | |
$ | 문자열의 맨 끝과 일치하는가?(옵션에 따라 의미가 다름) | |
\A | 문자열의 맨 처음과 일치하는가?(옵션에 따라 의미가 다름) | |
\Z | 문자열의 맨 끝과 일치하는가?(옵션에 따라 의미가 다름) | |
\b | 단어 구분자(whitespace) | |
\B | whitespace가 양쪽에 없는(사실 단어라고 볼수는 없다) 경우에 해당 문자가 있는 경우 매치 |
|
메타 문자는 or과 동일한 의미로 사용된다. A | B라는 정규식이 있다면 A 또는 B라는 의미가 된다. |
>>> p = re.compile('Crow|Servo')
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0, 4), match='Crow'>
^
메타 문자는 문자열의 맨 처음과 일치함을 의미한다. 앞에서 살펴본 컴파일 옵션 re.MULTILINE을 사용할 경우에는 여러 줄의 문자열일 때 각 줄의 처음과 일치하게 된다.
다음 예를 보자.
>>> print(re.search('^Life', 'Life is too short'))
<re.Match object; span=(0, 4), match='Life'>
>>> print(re.search('^Life', 'My Life'))
None
^Life
정규식은 Life 문자열이 처음에 온 경우에는 매치하지만 처음 위치가 아닌 경우에는 매치되지 않음을 알 수 있다.
$
$
메타 문자는 ^
메타 문자와 반대의 경우이다. 즉 $
는 문자열의 끝과 매치함을 의미한다. 다음 예를 보자.
>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12, 17), match='short'>
>>> print(re.search('short$', 'Life is too short, you need python'))
None
short$
정규식은 검색할 문자열이 short로 끝난 경우에는 매치되지만 그 이외의 경우에는 매치되지 않음을 알 수 있다.
\A
\A
는 문자열의 처음과 매치됨을 의미한다. ^
메타 문자와 동일한 의미이지만 re.MULTILINE
옵션을 사용할 경우에는 다르게 해석된다. re.MULTILINE 옵션을 사용할 경우 ^
은 각 줄의 문자열의 처음과 매치되지만 \A
는 줄과 상관없이 전체 문자열의 처음하고만 매치된다.
\Z
\Z
는 문자열의 끝과 매치됨을 의미한다. 이것 역시 \A
와 동일하게 re.MULTILINE
옵션을 사용할 경우 $
메타 문자와는 달리 전체 문자열의 끝과 매치된다.
\b
\b는 단어 구분자(Word boundary)이다. 보통 단어는 whitespace에 의해 구분된다.
>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))
<re.Match object; span=(3, 8), match='class'>
\bclass\b
정규식은 앞뒤가 whitespace로 구분된 class라는 단어와 매치됨을 의미한다. 따라서 no class at all
의 class라는 단어와 매치됨을 확인할 수 있다.
>>> print(p.search('the declassified algorithm'))
None
위 예의 the declassified algorithm 문자열 안에도 class 문자열이 포함되어 있긴 하지만 whitespace로 구분된 단어가 아니므로 매치되지 않는다.
>>> print(p.search('one subclass is'))
None
subclass 문자열 역시 class 앞에 sub 문자열이 더해져 있으므로 매치되지 않음을 알 수 있다.
\b
메타 문자를 사용할 때 주의해야 할 점이 있다. \b
는 파이썬 리터럴 규칙에 의하면 백스페이스(BackSpace)를 의미하므로 백스페이스가 아닌 단어 구분자임을 알려 주기 위해 r'\bclass\b'
처럼 Raw string임을 알려주는 기호 r을 반드시 붙여 주어야 한다.
\B
\B
메타 문자는 \b
메타 문자와 반대의 경우이다. 즉 whitespace로 구분된 단어가 아닌 경우에만 매치된다.
>>> p = re.compile(r'\Bclass\B')
>>> print(p.search('no class at all'))
None
>>> print(p.search('the declassified algorithm'))
<re.Match object; span=(6, 11), match='class'>
>>> print(p.search('one subclass is'))
None
class 단어의 앞뒤에 whitespace가 하나라도 있는 경우에는 매치가 안 되는 것을 확인할 수 있다.