정규 표현식
복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용함
파이썬 기초는 아니지만 배워두면 좋다~~
ex) 주민등록번호를 포함하고 있는 텍스트가 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해보자
ⓐ. 전체 텍스트를 공백 문자로 나눈다(split).
ⓑ. 나뉜 단어가 주민등록번호 형식인지 조사한다.
ⓒ. 단어가 주민등록번호 형식이라면 뒷자리를 *로 변환한다
ⓓ. 나뉜 단어를 다시 조립한다.
data = '''
park 800905-1049118
kwak 700905-1059119
'''
result = []
for line in data.split("\n"):
word_result = []
for word in line.split(" "):
if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
word = word[:6] + "-" + "*******"
word_result.append(word)
result.append(" ".join(word_result))
print("\n".join(result))
출력화면
import re
data = '''
park 800905-1049118
kwak 700905-1059119
'''
pat = re.compile("(\d{6})[-]\d{7}")
print(pat.sub("\g<1>-*******", data))
출력화면
(\d{6}) : 여섯 자리 숫자를 찾음, 괄호를 통해 그룹화했으며 이를 통해 나중에 참조할 수 있음
(\d{7}) : 하이픈(-)다음에 오는 7자리 숫자를 찾음, 괄호를 통해 그룹화 했으며 이를 통해 나중에 참조할 수 있음
pat = re.compile("(\d{6})[-]\d{7}"): data 문자열 내의 모든 패턴 일치 항목을 새로운 문자열로 대체, "\g<1>-*******"은 첫 번째 그룹 (\d{6})을 참조한 다음 하이픈 과 별표 7개로 대체함
정규 표현식에서 사용하는 메타 문자는 .^$+?{}\|() 등이 있음
메타문자란 원래 그 문자가 가진 뜻이아닌 특별한 용도로 사용하는 문자를 말함
차근차근 정규 표현식 메타 문자의 의미를 알아보자
① 문자 클래스 []
[] 사이 문자들과 매치 라는 의미를 가짐
ex 1) [abc]라면 -> a, b, c 중 한개의 문자와 매치라는 의미
ex 2)
정규식 [ABC]
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| a | Yes | "a"에는 정규식과 일치하는 문자 "a"가 있으므로 매치 |
| before | Yes | "before"에는 정규식과 일치하는 문자 "b"가 있으므로 매치 |
| dude | no | "dude"에는 정규식과 일치하는 문자인 a,b,c중 어느 하나도 없으므로 매치 x |
[] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위를 의미[a-c] 는 a,b,c [0-5]는 0,1,2,3,4,5를 의미[]를 사용할 때 ^ 메타 문자를 []안에서 사용하면 반대라는 의미를 갖게되므로 사용할대 주의해야함[^0-9] 숫자가 아닌 모든 문자가 매치됨자주 사용하는 정규 표현식
| 정규 표현식 | 설명 |
|---|---|
\d | 숫자와 매치, [0-9]와 동일한 표현식 |
\D | 숫자가 아닌것과 매치 [^0-9]와 동일한 표현식 |
\s | whitespace문자와 매치 [ \t\n\r\f\v]와 동일한 표현식, []에서 맨앞 빈칸은 공백 문자를 의미 |
\S | whitespace가 아닌것과 매치 [^ \t\n\r\f\v]과 동일한 표현식 |
\w | 문자 + 숫자와 매치, [a-zA-Z0-9_] 와 동일한 표현식 |
\W | 문자 + 숫자 아닌것과 매치 [^a-zA-Z0-9_] 와 동일한 표현식 |
② Dot (.)
.)은 메타 문자의 줄바꿈 문자인 \n을 제외한 모둔 문자와 매치됨을 의미a.b는 a와 b 사이에 줄바꿈 문자를 제외한 어떤 문자가 들어가도 매치됨ex 2)
정규식 [a.b]
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| aab | Yes | "aab"는 가운데 문자 "a"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치 |
| a0b | Yes | "a0b"는 가운데 문자 "0"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치 |
| abc | No | "abc"는 "a" 문자와 "b" 문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치 x |
a[.]b에서 [.]은 문자.만을 의미함 a.b와 헷갈리지 않도록 주의!! ③ 반복(*) *
*은 반복을 의미
* 바로 앞에 있는 문자가 0부터 무한대로 반복되면 매치
ex)
정규식 ca*t
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| ct | Yes | "a"가 0번 반복되어 매치 |
| cat | Yes | "a"가 0번 이상 반복되어 매치 |
| caaat | Yes | "a" 0번 이상 반복되어 매치 |
④. 반복(+)
+도 *가 마찬가지로 반복을 나타내는 정규표현식
+는 최소 1번 이상 반복될때마다 사용됨
+는 반복이 1부터 시작, *는 0부터 시작
ex)
정규식 ca+t
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| ct | No | "a"가 0번 반복 되어 매치x |
| cat | Yes | "a"가 1번 이상 반복되어 매치 |
| caaat | Yes | "a"가 1번이상 반복되어 매치 |
⑤. 반복({m,n}, ?)
반복 횟수를 제한하고 싶을때 사용할 수 있는 메타문자
{}메타문자를 사용하면 반복회수를 고정할 수 있음
{m,n}반복횟수가 m부터 n까지 매치됨
사용형식 1. {m}
ex) ca{2}t -> a가 2번 반복되면 매치
ca{2}t
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| cat | No | "a"가 1번 반복되어 매치x |
| caat | Yes | "a"가 2번 반복되어 매치 |
사용형식 2.{m,n}
ex) ca{2,5}t -> a가 2~5번 반복되면 매치
ca{2,5}t
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| cat | No | "a"가 1번만 반복되어 매치x |
| caat | Yes | "a"가 2번 반복되어 매치 |
| caaaaat | Yes | "a"가 5번 반복되어 매치 |
사용형식 3. ?
? -> {0,1}을 의미
ab?c
| 문자열 | 매치 여부 | 설명 |
|---|---|---|
| abc | Yes | "b"가 1번 사용되어 매치 |
| ac | Yes | "b"가 0번 사용되어 매치 |
파이썬은 정규 표현식을 지원하기 위해서 re(regular expression)모듈을 제공
re모듈은 파이선을 설치할 때 자동으로 설치되는 기본 라이브리러
사용할때는 re.compile(정규표현식)형식으로 사용
import re
p = re.compile('ab*')
re.compile된 패턴 객체를 사용하여 이후의 작업을 수행할 수 있음
문자열 검색을 위해 re모듈은 아래와 같은 메서드를 제공함
| 메서드 | 목적 |
|---|---|
| match() | 문자열의 처음부터 정규식과 매치되는지 조사한다. |
| search() | 문자열 전체를 검색하여 정규식과 매치되는지 조사한다. |
| findall() | 정규식과 매치되는 모든 문자열(substring)을 리스트로 반환한다. |
| findinter() | 정규식과 매치되는 모든 문자열(substring)을 반복가능한 객체로 반환한다. |
match, serach는 정규식과 매치될 때는 match객체를 반환해줌
매치되지 않는 경우에는 None을 반환
import re
pat = re.compile('[a-z]+')
#match
m = pat.match("python")
print(m)
m = pat.match("3 python")
print(m)
#search
m = pat.search("3 python")
print(m)
#findall
m = pat.findall("life is too short")
print(m)
#finditer
m = pat.finditer("life is too short")
print(m)
for r in m: print(r)
출력화면!
pat.match("python") 에서 python이란 문자열은 [a-z]+ 정규식에 부합되므로 match개체를 반환해주는 것을 확인할 수 있음
pat.match("3 python")에서는 매치되지 않으므로 None를 출력하는 것을 확인할 수 있음
m = pat.search("3 python") 입력된 문자열에 python이 존재하므로 그 에대한 정보를 담고있는 객체를 반환하고 그 정보를 출력하는것을 확인할 수 있다.
pat.findall("life is too short")는 주어진 문자열을 [a-z]+와 매치해서 리스트로 반환해주는것을 확인할 수 있음
pat.finditer("life is too short")은findall과 결과는 동일하지만 반복가능한 객체를 반환해주는거을 확인할 수 있음 반환된 객체는 match와search의 반환 객체와 동일한 객체 (match객체)
match 객체의 메서드를 사용하면 어떤 문자열이 매치되었는지, 매치된 문자열의 인덱스의 시작과 끝을 확인할 수 있음
| 메서드 | 목적 |
|---|---|
| group() | 매치된 문자열을 반환 |
| start() | 매치된 문자열의 시작 인덱스를 반환 |
| end() | 매치된 문자열의 끝 인덱스를 반환 |
| span() | 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 반환 |
import re
pat = re.compile("[a-z]+")
m = pat.match("python")
print(m.group())
print(m.start())
print(m.end())
print(m.span())
출력화면
정규식을 컴파일 할때 아래와 같은 옵션을 사용할 수 있음
| 옵션 이름 | 약어 | 설명 |
|---|---|---|
| DOTALL | S | dot문자(.)가 줄바꿈 문자를 포함하여 모든 문자와 매치 |
| IGNORECASE | I | 대문자, 소문자 관계없이 매치 |
| MULTILINE | M | 여러 줄과 매치 |
| VERBOSE | X | verbose모드를 사용(정규식의 가독성을 높이고 주석등을 사용할 수 있음) |
옵션을 사용할 때는 re.DOTALL or re.S 처럼 전체어 또는 약어를 사용해도 상관없음
①. DOTALL, S
. 메타 문자는 줄바꿈 문자를 제외한 모든 문자와 매치되는 규칙이 있음\n도 포함하여 매치하고 싶다면 re.DOTALL, re.S를 사용해 정규식을 컴파일 하면됨import re
p = re.compile('a.b')
m = p.match('a\nb')
print(m)
p = re.compile('a.b', re.DOTALL)
m = p.match('a\nb')
print(m)
출력화면
컴파일 옵션을 주지 않을때는 매치가 되지 않기 때문에 None을 반환한다 하지만 re.DOTALL 컴파일옵션을 주면 match객체가 반환되는것을 확인할 수 있음
re.DOTALL옵션은 여러 줄로 이루어진 문자열에서 \n에 상관없이 검색할 때 많이 사용함
②. IGNORECASE, I
import re
p = re.compile('[a-z]',re.I)
print(p.match('python'))
print(p.match('Python'))
print(p.match('PYTHON'))
출력화면
[a-z] 정규식은 소문자만 의미하지만, re.I 옵션으로 대소문자 구별없이 매치되는것을 확인할 수 있음③.MULTILINE, M
^,$ 와 연관된 옵션 -> ^는 문자열의 처음을 의미, $는 문자열의 마지막을 의미# MULTILINE 사용 x Example
import re
p = re.compile("^python\s\w+")
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data))
출력화면
정규식 ^python\s\w+은 python 이라는 문자열로 시작하고 그뒤에 whitespce, 그 뒤에 단어가 와야 한다는 의미
메타 문자 ^에 의해 python이라는 문자열을 사용한 첫 번째 줄만 매치됨
# MULTILINE을 사용한 경우 Example
import re
p = re.compile("^python\s\w+", re.MULTILINE)
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data))
출력화면
^ 메타 문자를 문자열 전체의 처음이 아니라 각 라인의 처음으로 인식시키고 싶은 경우에 사용함
^ 가 각라인에 첫줄에 적용된것을 확인할 수 있음
re.MULTILINE 또는 re.M 옵션은 ^,$ 메타 문자를 문자열의 각 줄마다 적용해줌
④. VERBOSE,V
import re
charref = re.compile(r'&[#](0[0-7])+|&#x[0-9a-fA-F]+;')
charref2 = re.compile(r"""
&[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
모두 동일한 정규식을 컴파일 하지만 두번재는 re.VERBOSE 옵션을 통해서 정규표현식의 가독성을 높이고 주석을 적어 보기 편하게 한것을 알 수 있다
re.VERBOSE옵션을 사용하면 문자열에 사용된 whitespace는 컴파일시 제거됨
줄다위로 #기호를 사용해 주석을 적을 수 있음
정규 표현식을 파이썬에서 사용할 때 혼란을 주는 요소가 바로 \(백슬래시)
\section이 [ \t\n\r\f\v]ection로 해석되어 매치가 제대로 이루어지지 않음
이런 문제를 해결하기 위해서는 \\ 처럼 백슬래시 2개를 사용하면됨
BUT, \\는 파이썬 문자열 리터럴 규칙에 따라 \로 변환되고 \\를 전달하기 위해서는 \\\\를 사용해야됨 -> Raw String으로 해결
p = re.compiler(r'//section') 처럼 앞에 문자열 앞에 r을 붙여서 사용하면 \\을 쉽게 전달할 수 있음