[Python] 파이썬 정규표현식 - re 모듈
이 포스팅은 다음 내용들에 대해 다룹니다.
- 정규 표현식
- 파이썬의 re 모듈
- 정규식을 이용한 문자열 검색
- 정규식을 이용한 문자열 치환과 분리
- match 객체의 메서드
- 그루핑
- 컴파일 옵션
- 백슬래시 문제
정규 표현식
정규 표현식의 기초, 메타 문자
정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것들이 있다.
. ^ $ * + ? { } [ ] \ | ( )
✋ 메타 문자란 원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자를 말한다.
문자 클래스 [ ]
[abc] 는 단어에 a,b,c 중 하나라도 있으면 매치된다.
[abc]는 [a-c]와 동일한 표현이며, 마찬가지로 [012345]는 [0-5]와 동일하다. 즉 하이픈(-)
은 ‘~’의 의미를 가진다.
- [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_]
와 동일한 표현식이다.
대문자로 사용된 것은 소문자의 반대임을 추측할 수 있다.
Dot(.)
정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n
을 제외한 모든 문자와 매치된다.
a.b
라는 정규 표현식은 a + 모든문자 + b와 매치된다.
예를 들면 다음과 같다.
- “aab”는 가운데 문자 “a”가 모든 문자를 의미하는
.
과 일치하므로 정규식과 매치된다. - “a0b”는 가운데 문자 “0”가 모든 문자를 의미하는
.
과 일치하므로 정규식과 매치된다. - “abc”는 “a”문자와 “b”문자 사이에 어떤 문자라도 하나는있어야 하는 이 정규식과 일치하지 않으므로 매치되지 않는다.
문자 ‘ . ‘ 그대로의 의미를 사용하고 싶다면, a[.]b
처럼 사용하면 된다. 이 정규표현식은 a.b와는 매치되고 acb와는 매치되지 않는다.
즉, 문자 클래스([ ]) 내에 Dot( . ) 메타 문자가 사용되면 문자 ‘ . ‘ 그대로를 의미한다.
반복(*)
*
메타 문자는 바로 앞에 있는 문자가 무한대로 반복될 수 있다는 의미이다.
예를 들어 ca*t
는 다음과 같은 문자열들과 모두 매치된다.
정규식 | 문자열 | 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”가 1번 이상 반복되어 매치 (1번 반복) |
ca+t |
caaat | Yes | “a”가 1번 이상 반복되어 매치 (3번 반복) |
반복 ({m,n}, ?)
{ }
메타 문자를 사용하면 반복 횟수를 고정할 수 있다. {m, n} 정규식을 사용하면 반복 횟수가 m부터 n까지 매치할 수 있다.
또한 m 또는 n을 생략할 수도 있다.
만약 {3,}
처럼 사용하면 반복 횟수가 3 이상인 경우이고 {,3}
처럼 사용하면 반복 횟수가 3 이하를 의미한다. 생략된 m은 0과 동일하며, 생략된 n은 무한대(2억 개 미만)의 의미를 갖는다.
반복은 아니지만 비슷한 개념으로 ?
메타 문자가 있다. 이 메타 문자는 {0,1}
과 동일한 의미를 갖는 것으로, 앞의 문자가 1번만 있거나 없거나라는 뜻이다.
예를 들면 다음과 같다.
정규식 | 문자열 | Match 여부 | 설명 |
---|---|---|---|
ab?c |
abc | Yes | “b”가 1번 사용되어 매치 |
ab?c |
ac | Yes | “b”가 0번 사용되어 매치 |
*
, +
, ?
메타 문자는 모두 {m, n}
형태로 고쳐 쓰는 것이 가능하지만 가급적 이해하기 쉽고 표현도 간결한 *
, +
, ?
메타 문자를 사용하는 것이 좋다.
|
|
메타 문자는 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로 끝난 경우에는 매치되지만 그 이외의 경우에는 매치되지 않음을 알 수 있다.
✋ ^
또는 $
문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 \^
, \$
로 사용하면 된다.
파이썬의 re 모듈
파이썬은 정규 표현식을 지원하기 위해 re ^regular\ expression^ 모듈을 제공한다.
import re
p = re.compile('ab*')
re.compile을 사용하여 정규 표현식(위 예에서는 ab*
)을 컴파일한다. re.compile의 결과로 돌려주는 객체 p(컴파일된 패턴 객체)를 사용하여 그 이후의 작업을 수행할 수 있다.
정규식을 이용한 문자열 검색
컴파일된 패턴 객체의 메서드를 사용하여 문자열 검색을 수행할 수 있다.
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>
m = p.match("3 python")
print(m)
None
일반적으로 다음과 같이 사용됩니다.
p = re.compile('[a-z]+')
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
out:
Match found: string
search
search 메서드는 문자열 전체를 검사하기 때문에 다음과 같은 결과가 나타납니다.
m = p.search("python")
print(m)
<_sre.SRE_Match object at 0x01F3FA68>
m = p.search("3 python")
print(m)
<_sre.SRE_Match object at 0x01F3FA30>
findall
findall 메서드는 문자열 전체를 검사하여 정규식과 매치해서 리스트로 반환합니다.
result = p.findall("life is too short")
print(result)
['life', 'is', 'too', 'short']
finditer
finditer 메서드는 findall과 동일하지만 정규식과 매치되는 각각의 요소를 match 객체로 반환합니다.
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>
정규식을 이용한 문자열 치환과 분리
sub, subn
sub와 subn은 문자열에서 정규식과 매치되는 요소들을 원하는 요소로 치환해주며, subn
은 반환하는 값이 치환된 문자열과 더불어 치환된 개수의 튜플이라는 점 이외에는 sub
와 동일합니다.
sub 메서드는 다음과 같은 파라미터들을 갖습니다.
repl
: 대신할 문자열string
: 대상 문자열count
: 치환 횟수
사용법은 다음과 같습니다.
s = 'Gorio, Gorio, Gorio keep a straight face.'
p = re.compile(r'Gorio')
replaced_s = p.sub( repl='Ryan', count=2, string=s)
print(replaced_s)
out:
Ryan, Ryan, Gorio keep a straight face.
split
split 메서드는 파이썬 문자열의 기본 메서드인 split과 유사하지만 정규식을 처리할 수 있습니다.
s = "100-200*300-500+20"
p = re.compile(r'\D')
p.split(s)
out:
['100', '200', '300', '500', '20']
만약 구분자까지 포함하는 리스트를 얻고 싶다면, ( )
메타 문자로 그루핑을 사용할 수 있습니다(그루핑은 뒤에서 설명합니다).
s = "100-200*300-500+20"
p = re.compile(r'(\D)')
p.split(s)
out:
['100', '-', '200', '*', '300', '-', '500', '+', '20']
match 객체의 메서드
match 객체는 다음곽 같은 메서드들을 가집니다.
method | 목적 |
---|---|
group() | 매치된 문자열을 돌려준다. |
start() | 매치된 문자열의 시작 위치를 돌려준다. |
end() | 매치된 문자열의 끝 위치를 돌려준다. |
span() | 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다. |
예를 들면 다음과 같습니다.
m = p.match("python")
m.group()
'python'
m.start()
0
m.end()
6
m.span()
(0, 6)
match 메서드는 문자열의 처음부터 검색하기 때문에 start( ) 메서드의 결과는 항상 0일 수 밖에 없습니다.
search 메서드의 경우 다음과 같은 결과가 나올 수 있습니다.
m = p.search("3 python")
m.group()
'python'
m.start()
2
m.end()
8
m.span()
(2, 8)
✋ 모듈 단위로 수행하기
지금까지 re.compile 로 생성한 패턴 객체를 이용해 match, search 등의 메서드를 사용했지만 이를 다음과 같이 축약된 형태로 사용할 수 있습니다.
p = re.compile('[a-z]+')
m = p.match("python")
# 같은 표현
m = re.match('[a-z]+', "python")
특정 패턴을 한 번만 사용할 경우에 편리합니다.
그루핑
ABC 문자열이 계속해서 반복되는지 조사하는 정규식을 작성하고 싶다고 하자. 어떻게 해야할까? 지금까지 공부한 내용으로는 위 정규식을 작성할 수 없다. 이럴 때 필요한 것이 바로 그루핑(Grouping) 이다.
위 경우는 다음처럼 그루핑을 사용하여 작성할 수 있다.
(ABC)+
그룹을 만들어 주는 메타 문자는 바로 ( )
이다.
p = re.compile('(ABC)+')
m = p.search('ABCABCABC OK?')
print(m)
<re.Match object; span=(0, 9), match='ABCABCABC'>
print(m.group())
ABCABCABC
컴파일 옵션
정규식을 컴파일할 때 다음 옵션을 사용할 수 있다.
- DOTALL(S) -
.
이 줄바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 한다. - IGNORECASE(I) - 대소문자에 관계없이 매치할 수 있도록 한다.
- MULTILINE(M) - 여러줄과 매치할 수 있도록 한다. (
^
,$
메타문자의 사용과 관계가 있는 옵션이다) - VERBOSE(X) - verbose 모드를 사용할 수 있도록 한다. (정규식을 보기 편하게 만들수 있고 주석등을 사용할 수 있게된다.)
옵션을 사용할 때는 re.DOTALL
처럼 전체 옵션 이름을 써도 되고 re.S
처럼 약어를 써도 된다.
백슬래시 문제
정규 표현식을 파이썬에서 사용할 때 혼란을 주는 요소가 한 가지 있는데, 바로 백슬래시(\
)이다.
예를 들어 어떤 파일 안에 있는 "\section"
문자열을 찾기 위한 정규식을 만든다고 가정해 보자.
\section
이 정규식은 \s
문자가 whitespace로 해석되어 의도한 대로 매치가 이루어지지 않는다.
위 표현은 다음과 동일한 의미이다.
[ \t\n\r\f\v]ection
의도한 대로 매치하고 싶다면 다음과 같이 변경해야 한다.
\\section
즉 위 정규식에서 사용한 \
문자가 문자열 자체임을 알려 주기 위해 백슬래시 2개를 사용하여 이스케이프 처리를 해야 한다.
따라서 위 정규식을 컴파일하려면 다음과 같이 작성해야 한다.
p = re.compile('\\section')
그런데 여기에서 또 하나의 문제가 발견된다. 위처럼 정규식을 만들어서 컴파일하면 실제 파이썬 정규식 엔진에는 파이썬 문자열 리터럴 규칙에 따라 \\
이 \
로 변경되어 \section
이 전달된다.
✋ 이 문제는 위와 같은 정규식을 파이썬에서 사용할 때만 발생한다(파이썬의 리터럴 규칙). 유닉스의 grep, vi 등에서는 이러한 문제가 없다.
결국 정규식 엔진에 \\
문자를 전달하려면 파이썬은 \\\\
처럼 백슬래시를 4개나 사용해야 한다.
✋ 정규식 엔진은 정규식을 해석하고 수행하는 모듈이다.
p = re.compile('\\\\section')
만약 위와 같이 \
를 사용한 표현이 계속 반복되는 정규식이라면 너무 복잡해서 이해하기 쉽지않을 것이다. 이러한 문제로 인해 파이썬 정규식에는 Raw String 규칙이 생겨나게 되었다. 즉 컴파일해야 하는 정규식이 Raw String임을 알려 줄 수 있도록 파이썬 문법을 만든 것이다. 그 방법은 다음과 같다.
p = re.compile(r'\\section')
위와 같이 정규식 문자열 앞에 r 문자를 삽입하면 이 정규식은 Raw String 규칙에 의하여 백슬래시 2개 대신 1개만 써도 2개를 쓴 것과 동일한 의미를 갖게 된다.
✋ 만약 백슬래시를 사용하지 않는 정규식이라면 r의 유무에 상관없이 동일한 정규식이 될 것이다.
위 내용은 여기를 참조하였으며, 더 많은 내용을 확인하실 수 있습니다.
Leave a comment