원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자
문자 클래스 []
[ ] 사이에 있는 각각의 문자들에 대해 적어도 하나가 매치가 되는가?
즉, 정규 표현식이 [abc]라면 이 표현식의 의미는 “a, b, c 중 한 개의 문자와 매치”를 뜻한다.
- “a”는 정규식과 일치하는 문자인 “a”가 있으므로 매치
- “before”는 정규식과 일치하는 문자인 “b”가 있으므로 매치
- “dude”는 정규식과 일치하는 문자인 a, b, c 중 어느 하나도 포함하고 있지 않으므로 매치되지 않음
[ ] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위(From - To)를 의미한다. 예를 들어 [a-c]라는 정규 표현식은 [abc]와 동일하고 [0-5]는 [012345]와 동일하다.
[a-zA-Z]: 알파벳 모두[0-9]: 숫자
문자 클래스([ ]) 안에는 어떤 문자나 메타 문자도 사용할 수 있다. 하지만..
주의사항
주의해야 할 메타 문자가 1가지 있다. 그것은 바로 ^인데, 문자 클래스 안에 ^ 메타 문자를 사용할 경우에는 반대(not)라는 의미를 갖는다. 예를 들어 [^0-9]라는 정규 표현식은 숫자가 아닌 문자만 매치된다.
자주 사용하는 문자 클래스
| 메타문자 | 동일 표현식 | 설명 |
|---|---|---|
| \d | [0-9] | 숫자와 매치 |
| \D | [^0-9] | 숫자가 아닌 것과 매치 |
| \s | [ \t\n\r\f\v] | whitespace 문자(스페이스, 공백, tab)와 매치(맨 앞의 빈 칸은 공백문자(space)를 의미한다.) |
| \S | [^ \t\n\r\f\v] | whitespace 문자가 아닌 것과 매치 |
| \w | [a-zA-Z0-9_] | 문자+숫자(alphanumeric)와 매치 |
| \W | [^a-za-z0-9_] | 문자+숫자(alphanumeric)가 아닌문자와 매치 |
대문자 사용은 소문자의 반대이다.
Dot(.)
줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨을 의미, 단 하나라도 있어야 한다.
사실, re.DOTALL 옵션을 주면 \n 문자와도 매치된다. 일단 예제를 보며 이해해보자.
| 정규식 | 의미 |
|---|---|
a.b | ”a + 모든문자 + b” |
즉, a와 b라는 문자 사이에 어떤 문자가 들어가도 모두 매치된다는 의미이다. 문자열 “aab”, “a0b”, “abc”가 정규식 a.b와 어떻게 매치되는지 살펴보자.
- “aab”는 가운데 문자 “a”가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치된다.
- “a0b”는 가운데 문자 “0”가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치된다.
- “abc”는 “a”문자와 “b”문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치되지 않는다.
마지막이 헷갈린다. 조심하도록 하자. 그런데 만약 ”.” 문자 자체와 매칭이 되길 원한다면 어떻게 해야할까?
| 정규식 | 의미 |
|---|---|
a[.]b | ”a + Dot(.)문자 + b” |
정규식 a[.]b는 “a.b” 문자열과 매치되고, “a0b” 문자열과는 매치되지 않는다. 혼동하지 말자.
반복(*)
특정 문자가 0부터 무한대까지의 반복을 매칭한다.
| 정규식 | 문자열 | Match 여부 | 설명 |
|---|---|---|---|
ca*t | ct | Yes | ”a”가 0번 반복되어 매치 |
ca*t | cat | Yes | ”a”가 0번 이상 반복되어 매치 (1번 반복) |
ca*t | caaat | Yes | ”a”가 0번 이상 반복되어 매치 (3번 반복) |
반복(+)
특정 문자가 1부터 무한대까지의 반복을 매칭한다.
| 정규식 | 문자열 | Match 여부 | 설명 |
|---|---|---|---|
ca+t | ct | No | ”a”가 0번 반복되어 매치되지 않음 |
ca+t | cat | Yes | ”a”가 0번 이상 반복되어 매치 (1번 반복) |
ca+t | caaat | Yes | ”a”가 0번 이상 반복되어 매치 (3번 반복) |
반복({n,m})
특정 문자가 n이상 m이하 반복 횟수인 경우 매칭한다.
{ } 메타 문자를 사용하면 반복 횟수를 고정할 수 있다. {m, n} 정규식을 사용하면 반복 횟수가 m부터 n까지 매치할 수 있다. 또한 m 또는 n을 생략할 수도 있다. 만약 {3,}처럼 사용하면 반복 횟수가 3 이상인 경우이고 {,3}처럼 사용하면 반복 횟수가 3 이하를 의미한다. 생략된 m은 0과 동일하며, 생략된 n은 무한대(2억 개 미만)의 의미를 갖는다.
{1,}은 +와 동일하고, {0,}은 *와 동일하다. 이 부분을 언급하는 이유는, 굳이 저런 표현보다는 +, *을 사용하는 것이 직관적이기 때문이다.
{n}
반드시 n번 반복.
| 정규식 | 문자열 | Match 여부 | 설명 |
|---|---|---|---|
ca{2}t | cat | No | ”a”가 1번만 반복되어 매치되지 않음 |
ca{2}t | caat | Yes | ”a”가 2번 반복되어 매치 |
{n, m}
n~m 번 반복.
| 정규식 | 문자열 | Match 여부 | 설명 |
|---|---|---|---|
ca{2,5}t | cat | No | ”a”가 1번만 반복되어 매치되지 않음 |
ca{2,5}t | caat | Yes | ”a”가 2번 반복되어 매치 |
ca{2,5}t | caaaaat | Yes | ”a”가 5번 반복되어 매치 |
?
있어도 되고 없어도 된다.
| 정규식 | 문자열 | Match 여부 | 설명 |
|---|---|---|---|
ab?c | abc | Yes | ”b”가 1번 사용되어 매치 |
ab?c | ac | Yes | ”b”가 0번 사용되어 매치 |
문자열 소비
정규식을 공부하다 보면 Assertion 이라는 단어를 자주 보게된다. Assertion이라는 단어는 검사, 검증, 확인 이라는 의미를 갖고 있다. 결과적으로 어떤 것을 검사하겠다는 의미로 많이 사용한다. 그런데 Zero-width는 무엇인가? 너비가 없다는 의미인데 왜 이런 추상적인 단어를 썼는지는 아직도 모르겠다. 하지만 말을 끼워 맞춰서 연상을 쉽게 해보도록 하자.
이 개념을 알기 전에 우리가 알아야 할 것이 있다. 지금 이 글을 앞의 메타 문자와 따로 쓴 이유가 여기에 있다. 앞에서 배운 메타문자를 사용하게 되면 우리는 문자열을 소비한다. 이게 무슨 소리일까.
이 굉장히 추상적인 개념은 예시를 들어 한번만 생각하면 굉장히 쉽게 이해가 가능하다. 예를 들어 아버지가방에들어간다 라는 문자열이 있다고 해보자. 내가 궁금한 정보는 가방을 찾고 방에 를 찾고 싶다. 이러한 문제에 대해 [가방] 이렇게 정규식을 작성하고 검색을 진행했을 때, 정규식 엔진이 어떤 방식으로 구동하는지를 따라가 보자.
아버지부분 까지는 매치가 되지 않으므로 그냥 지나간다.가방을 확인하고는 매치되었으므로 문자열을 소비해 버린다. 즉 없애 버린다와 같은 개념으로 보는 것이다.- 소비된 문자열은 쳐다도 안보고 그 다음 문자열인
에들어간다라는 문자열에 대해가방문자열을 찾는다. 가방을 찾는 정규표현식은 끝난다.방에를 찾는 정규표현식이 시작되고, 찾지 못한 상태로 끝난다.
이 예시를 보다보면, 소비라는 것이 어떤 의미로 사용되었는지 이해할 수 있다. 즉, 매치가 되는 경우 해당 부분이 없어지게 된다. 정말 소비라는 단어에 걸맞는 행동이다.
Zero-width Assertion
그렇다면, 너비가 없는 확인 이라는 의미의 Zero-width Assertion은 무엇일까. 위에서 가방이라는 단어는 2의 너비를 가진다고 볼 수 있다. 그리고 매칭이 되었을 때, 이 2의 너비에 해당하는 문자열이 소비된다. 이러한 관점에서 보았을 때, 0의 너비를 가진다고 함은, 검증을 진행하는데 있어 소비가 되는 문자열이 없다라는 의미로 와닿는다. 결과적으로 이러한 해석이 맞으며, Zero-width Assertion는 검색을 진행하는데 있어 위에서 알아본 문자열 소비가 없는 방법을 말한다. 이제 이러한 종류의 메타 문자에 대해서 알아보자.
^ 또는 문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 \^, \ 로 사용하면 된다.
| 메타문자 | 설명 |
|---|---|
| ^ | 문자열의 맨 처음과 일치하는가?(옵션에 따라 의미가 다름) |
| $ | 문자열의 맨 끝과 일치하는가?(옵션에 따라 의미가 다름) |
| \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'))
Noneshort$ 정규식은 검색할 문자열이 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'))
Nonesubclass 문자열 역시 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'))
Noneclass 단어의 앞뒤에 whitespace가 하나라도 있는 경우에는 매치가 안 되는 것을 확인할 수 있다.