정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용한다. 최소한 이런의미로 쓰였구나 라는건 알아볼 필요가 있다고 생각해서 간단히 정리해보려고한다.
파이썬에서는 정규 표현식을 사용하기 위해 re(regular expression)모듈
을 제공하고 있다.
import re
re모듈을 이용하여 우리가 작성하는 정규표현식을 컴파일한다.
메타 문자 : 원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자
. ^ $ * + ? { } [ ] \ | ( )
정규 표현식에 위 메타 문자를 사용하면 특별한 의미를 갖게 된다.
문자 클래스로 만들어진 정규식은 "[ ] 사이의 문자들과 매치"라는 의미를 갖는다.
ex)정규 표현식이 [abc] -> "a, b, c 중 한 개의 문자와 매치"를 뜻한다.
위의 [abc]를 가지고
"a"는 정규식과 일치하는 문자인 "a"가 있으므로 매치
"before"는 정규식과 일치하는 문자인 "b"가 있으므로 매치
"dude"는 정규식과 일치하는 문자인 a, b, c 중 어느 하나도 포함하고 있지 않으므로 매치되지 않음
[ ] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위(From - To)를 의미한다.
[a-zA-Z] : 알파벳 모두
[0-9] : 숫자
^
주의! , []안에 ^
를 사용할 경우 이것은 (not)라는 의미를 갖는다. 예를 들어 [^0-9]라는 정규 표현식은 숫자가 아닌 문자만 매치된다.
[0-9] 또는 [a-zA-Z] 등은 무척 자주 사용하는 정규 표현식이다. 이렇게 자주 사용하는 정규식은 별도의 표기법으로 표현할 수 있다. 다음을 기억해 두자.
\d - 숫자와 매치, [0-9]와 동일한 표현식이다.
\D - 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식이다.
\s - whitespace 문자와 매치, [ \t\n\r\f\v]와 동일한 표현식이다. 맨 앞의 빈 칸은 공백문자(space)를 의미한다.
\S - whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식이다.
\w - 문자+숫자(alphanumeric)와 매치, [a-zA-Z0-9_]와 동일한 표현식이다.
\W - 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식이다.
대문자로 사용된 것은 소문자의 반대임을 추측할 수 있다.
a.b
위 정규식의 의미는 다음과 같다.
"a + 모든문자 + b"
즉 a와 b라는 문자 사이에 어떤 문자가 들어가도 모두 매치된다는 의미이다.
"aab"는 가운데 문자 "a"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치된다.
"a0b"는 가운데 문자 "0"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치된다.
"abc"는 "a"문자와 "b"문자 사이에 어떤 문자라도 하나는있어야 하는 이 정규식과 일치하지 않으므로 매치되지 않는다.
ca*t
이 정규식에는 반복을 의미하는 메타 문자가 사용되었다. 여기에서 사용한 은 *
바로 앞에 있는 문자 a가 0부터 무한대로 반복될 수 있다는 의미이다(한.. 2억개정도반복가능)
ca*t
ct Yes "a"가 0번 반복되어 매치
ca*t
cat Yes "a"가 0번 이상 반복되어 매치 (1번 반복)
ca*t
caaat Yes "a"가 0번 이상 반복되어 매치 (3번 반복)
🤔반복횟수를 지정하고 싶다면?
{ } 메타 문자를 사용하면 반복 횟수를 고정할 수 있다. {m, n} 정규식을 사용하면 반복 횟수가 m부터 n까지 매치할 수 있다. 또한 m 또는 n을 생략할 수도 있다. 만약 {3,}처럼 사용하면 반복 횟수가 3 이상인 경우이고 {,3}처럼 사용하면 반복 횟수가 3 이하를 의미한다. 생략된 m은 0과 동일하며, 생략된 n은 무한대(2억 개 미만)의 의미를 갖는다.
반복은 아니지만 이와 비슷한 개념으로 ? 이 있다. ? 메타문자가 의미하는 것은 {0, 1} 이다.
ab?c
위 정규식의 의미는 다음과 같다 -> "a + b(있어도 되고 없어도 된다) + c"
ab?c ->abc : Yes("b"가 1번 사용되어 매치)
ab?c ->ac : Yes("b"가 0번 사용되어 매치)
즉 b 문자가 있거나 없거나 둘 다 매치되는 경우이다.
이제 컴파일된 패턴 객체를 사용하여 문자열 검색을 수행해 보자. 컴파일된 패턴 객체는 다음과 같은 4가지 메서드를 제공한다.
Method | 목적 |
---|---|
match() | 문자열의 처음부터 정규식과 매치되는지 조사한다. |
search() | 문자열 전체를 검색하여 정규식과 매치되는지 조사한다. |
findall() | 정규식과 매치되는 모든 문자열(substring)을 리스트로 돌려준다. |
finditer() | 정규식과 매치되는 모든 문자열(substring)을 반복 가능한 객체로 돌려준다. |
match, search는 정규식과 매치될 때는 match 객체를 돌려주고, 매치되지 않을 때는 None을 돌려준다.
import re
p = re.compile('[a-z]+')
match 메서드는 문자열의 처음부터 정규식과 매치되는지 조사한다. 위 패턴에 match 메서드를 수행해 보자.
m = p.match("python")
print(m)
<_sre.SRE_Match object at 0x01F3F9F8>
"python" 문자열은 [a-z]+ 정규식에 부합되므로 match 객체를 돌려준다.
정규식에 부합되지 않으면 None을 돌려준다.
문자열 전체를 검색하여 정규식과 매치되는지 조사한다.
컴파일된 패턴 객체 p를 가지고 이번에는 search 메서드를 수행해 보자.
>>> m = p.search("python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA68>
"python" 문자열에 search 메서드를 수행하면 match 메서드를 수행했을 때와 동일하게 매치된다.
>>> m = p.search("3 python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA30>
"3 python" 문자열의 첫 번째 문자는 "3"이지만 search는 문자열의 처음부터 검색하는 것이 아니라 문자열 전체를 검색하기 때문에 "3 " 이후의 "python" 문자열과 매치된다.
이렇듯 match 메서드와 search 메서드는 문자열의 처음부터 검색할지의 여부에 따라 다르게 사용해야 한다.
정규식과 매치되는 모든 문자열(substring)을 리스트로 돌려준다.
result = p.findall("life is too short")
print(result)
['life', 'is', 'too', 'short']
"life is too short" 문자열의 'life', 'is', 'too', 'short' 단어를 각각 [a-z]+ 정규식과 매치해서 리스트로 돌려준다.
정규식과 매치되는 모든 문자열(substring)을 반복 가능한 객체로 돌려준다.
>>> result = p.finditer("life is too short")
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result: print(r)
...
<_sre.SRE_Match object at 0x01F3F9F8>
<_sre.SRE_Match object at 0x01F3FAD8>
<_sre.SRE_Match object at 0x01F3FAA0>
<_sre.SRE_Match object at 0x01F3F9F8>
finditer는 findall과 동일하지만 그 결과로 반복 가능한 객체(iterator object)를 돌려준다. 반복 가능한 객체가 포함하는 각각의 요소는 match 객체이다.
^: 문자열의 시작을 의미
$ : 문자열의 끝을 의미