파이썬 정규표현식 (Python Regular Expression)

5 minute read

데이터를 다루면서 정규표현식(Regular Expression)을 가장 유용하게 썼던 경험은 제품 모델명을 필터링 할 때였다. 알파벳, 숫자, -혹은 _로 이루어진 모델명은 모델 종류에 따라 나름의 패턴이 있다. 예를 들어 여성복 TIME의 경우 아래와 같은 모델명이 있다.

TM2A9WBL742W_BK

TM은 TIME 브랜드 명, WBL은 여성 블라우스라는 뜻, 마지막 BK는 BLACK 색상을 의미한다.

TM2A4WSC61WP3_BK

TM은 TIME 브랜드 명, WSC는 여성 스커트라는 뜻, 마지막 BK는 BLACK 색상이다. 첫 번째 예시보다 길이가 더 길다.

만약 TIME의 BLACK 색상에 해당하는 제품 명만 필터링 하고 싶다면 위 두 예시를 포괄하는 정규표현식(Regular Expression)을 이렇게 작성할 수 있다.

TM[a-zA-Z0-9]{10,11}_BK

TM 뒤에 알파벳 소문자 혹은 대문자 혹은 숫자가 10개 이상 11개 이하로 오고 _BK로 끝나는 패턴을 찾으라는 뜻이다.

이처럼 다양한 형태의 모델명을 필터링해서 원하는 정보만 가져오고자 할 때 정규표현식(Regular Expression)을 쓰면 쉽게 해결할 수 있다. 이 포스트에서는 먼저 정규표현식(Regular Expression) 작성하는 법을 살펴보고 이어서 정규표현식(Regular Expression) 활용 메소드를 다룬다.

정규표현식(Regular Expression) 작성하는 법

Python 문자열(string)을 작성할 때 작은 따옴표로 감싸듯이 정규표현식(Regular Expression) 역시 'google' 과 같은 식으로 작은 따옴표 안에 작성하면 된다. 단, 반복되는 패턴이 있는 부분은 'go{2,4}gle' (알파벳 o를 2~4번 반복하라는 뜻) 과 같이 문자열 뒤에 반복 회수를 지정해준다.

문자

기호 의미
. \n을 제외한 모든 문자
[.] 마침표
[a-z] 알파벳 소문자 중 하나
[A-Z] 알파벳 대문자 중 하나
[a-zA-Z] 알파벳 전체 중 하나
[0-9] 숫자 중 하나
[a-zA-Z0-9] 알파벳 전체, 숫자 전체 중 하나
\d 숫자 ([0-9]와 같은 표현)
\w 알파벳 전체, 숫자 전체, 언더바 ([a-zA-Z0-9_]와 같은 표현)
\s 공백


반복 회수

기호 의미 예시 비고
? 0 or 1회 go?gle ggle, gogle 과 일치
* 0 or 1회 이상 go*gle ggle, gogle, google, gooogle 등과 일치
+ 1회 이상 go+gle gogle, google, gooogle 등과 일치
{m} m회 go{2}gle google 과 일치
{m,} m회 이상 go{2,}gle google, gooogle, goooogle 과 일치
{m,n} m회 이상 n회 이하 go{2,3}gle google, gooogle 과 일치


기타

기호 의미
^ []안에서 쓰이면 []안의 문자를 제외한 나머지라는 뜻
''안에서 쓰이면 패턴의 시작을 나타냄
$ ''안에서 쓰이면 패턴의 끝을 나타냄
| []안에서 쓰이면 or의 의미
() 패턴을 그룹화할 때 사용


예시

import re

# Phone number
s = '832-38-1847'
digit_pattern = re.match('\d{3}-*\d{2}-*\d{3}', s)
print(digit_pattern)

# result
<re.Match object; span=(0, 10), match='832-38-184'>

설명 :
\d{3} 숫자 3개가 와야 함, -* 하이픈은 있어도 되고 없어도 됨

# Name
s = 'Rich Salamander Vuduc'
name_pattern = re.match('^[a-zA-Z]+\s+([a-zA-Z]+\s)?[a-zA-Z]+$', s)
print(name_pattern)

# result
<re.Match object; span=(0, 21), match='Rich Salamander Vuduc'>

설명 :
^ string이 바로 뒤에 이어지는 패턴으로 시작해야 함 [a-zA-Z]+ First name은 알파벳이 1개 이상(+) \s+ First name 뒤에 공백이 1개 이상(+) 올 수 있음 ([a-zA-Z]+\s) Middle name은 알파벳 1개 이상(+) 그리고 공백이 1개 올 수 있음 ? Middle name은 있어도 되고 없어도 됨 [a-zA-Z]+ Last name은 알파벳이 1개 이상(+) 임 $ string이 바로 앞의 패턴으로 끝나야 함

# Email
s = 'test_email@gmail.com'
email_pattern = re.match('^[a-zA-Z][a-zA-Z0-9.\-_+]*@[a-zA-Z0-9.\-_]+[a-zA-z]$', s)
print(email_pattern)

# result
<re.Match object; span=(0, 20), match='test_email@gmail.com'>

설명 :
^[a-zA-Z] 알파벳으로 시작해야 함 [a-zA-Z0-9.\-_]* 알파벳, 숫자, 마침표, -, _ 에 해당하는 문자가 0 또는 1개 이상(*) 올 수 있음 @ @ 문자가 와야 함 [a-zA-Z0-9.\-_]+ 알파벳, 숫자, 마침표, -, _ 에 해당하는 문자가 1개 이상(+) 와야 함 [a-zA-z]$ 알파벳으로 끝나야 함

# Example
s = 'new_ep_f014'
example_pattern = re.match('new_?[a-z]+_[f|m]\d{2,4}', s)
print(example_pattern)

# result
<re.Match object; span=(0, 11), match='new_ep_f014'>

설명 :
new new로 시작 _? 언더바는 있어도 되고 없어도 됨 [a-z]+ 알파벳 소문자가 1개 이상(+) 옴 [f|m] f 혹은 m 문자가 와야 함 \d{2,4} 숫자가 2개 이상 4개 이하 와야 함

정규표현식(Regular Expression) 활용 메소드

정규표현식 활용 메소드에는 match, search, findall, finditer, sub 등이 있다.

1. match

위에서는 digit_pattern = re.match('\d{3}-*\d{2}-*\d{3}', s)와 같이 코드 한 줄로 패턴을 찾는 방법을 썼지만, 정규표현식(Regular Expression)을 여러번 반복해서 쓸 경우 아래 예시와 같이 compile을 활용하는 것이 좋다.

import re  

# 제품명 목록
model_name = ['TM2A4WSC61WP3_BK', 'TM2A4WSC61WP33_BK', 'TM2A4WSC61WP3_OR', 'TM2A4WSC61WP3BK']

# 찾고자 하는 패턴을 정의해서 time_pattern 이라는 이름으로 저장한다.
model_pattern = re.compile('TM[a-zA-Z0-9]{10,11}_BK')

# time_string의 4개 모델명을 time_pattern에 대입해보고 일치하면 그 결과를, 아니면 None을 반환한다.
for s in model_name:
  print(model_pattern.match(s))
  
# result
<re.Match object; span=(0, 16), match='TM2A4WSC61WP3_BK'>
None
None
None

1번째 제품명은 패턴과 일치했다. 2번째 제품명은 ‘TM’과 ‘_BK’ 사이에 알파벳 혹은 숫자가 12개 오기 때문에 None이 리턴되었다. 3번째 제품명은 ‘_OR’로 끝나기 때문에 None이 리턴되었다. 마지막 5번째 제품명은 모델명에 ‘_‘가 없어서 None이 리턴되었다.

모델명 'TM2A4WSC61WP3_BK'에서 품목(WSC)과 색상(BK)만 추출하고 싶다면 괄호를 사용해 그룹화 할 수 있다.

# 괄호로 그룹화 하는 예
s = 'TM2A4WSC61WP3_BK'
model_pattern = re.match('\w{5}([A-Z]{3})\w+_([A-Z]{2})', s)
print(model_pattern.group())
print(model_pattern.group(0))
print(model_pattern.group(1))
print(model_pattern.group(2))

# result
TM2A4WSC61WP3_BK
TM2A4WSC61WP3_BK
WSC
BK

group()group(0)는 패턴과 일치한 전체 문자열을 반환하고 group(1)은 첫번째 괄호에 해당하는 문자열, group(2)는 두번째 괄호에 해당하는 문자열을 반환한다.

괄호 안에 ?P<name>을 써서 그룹에 이름을 지정해 줄 수도 있다.

# ?P<name>으로 그룹에 이름 부여하기
s = 'TM2A4WSC61WP3_BK'
model_pattern = re.match('\w{5}(?P<category>[A-Z]{3})\w+_(?P<color>[A-Z]{2})', s)
print(model_pattern.group('category'))
print(model_pattern.group('color'))

# result
WSC
BK

group() 외에도 start(), end(), span()을 써서 일치하는 패턴의 위치를 파악할 수 있다.

s = 'TM2A4WSC61WP3_BK'
model_pattern = re.match('\w{5}(?P<category>[A-Z]{3})\w+_(?P<color>[A-Z]{2})', s)

# 첫번째 패턴 WSC에 해당하는 start, end, span 위치 찾기
print(model_pattern.start(1))
print(model_pattern.end(1))
print(model_pattern.span(1))

# result
5
8
(5, 8)

문자열의 처음과 끝이 모두 정규표현식(Regular Expression)과 일치해야만 하는 match와는 달리 search는 전체 문자열에서 원하는 패턴과 일치하는 부분을 골라준다.

s = 'With Deal:	$5.94 + No Import Fees Deposit & $7.46 Shipping to Korea, Republic of'
price_pattern = re.search('\$\d[.]\d{2}', s)
print(price_pattern)
print(price_pattern.group())
print(price_pattern.start())
print(price_pattern.end())
print(price_pattern.span())

# result
<re.Match object; span=(11, 16), match='$5.94'>
$5.94
11
16
(11, 16)

3. findall

정규표현식(Regular Expression)과 일치하는 모든 문자열을 찾아 list로 반환한다.

s = 'With Deal:	$5.94 + No Import Fees Deposit & $7.46 Shipping to Korea, Republic of'
price_pattern = re.findall('\$\d[.]\d{2}', s)
print(price_pattern)

# result
['$5.94', '$7.46']

4. finditer

만약 정규표현식(Regular Expression)과 일치하는 모든 문자열을 찾아, group(), start(), end(), span() 메소드로 얻을 수 있는 정보를 추출하고 싶다면 finditer를 써야 한다.

s = 'With Deal:	$5.94 + No Import Fees Deposit & $7.46 Shipping to Korea, Republic of'
price_pattern = re.finditer('\$\d[.]\d{2}', s)

for m in price_pattern:
    print("---------------")
    print(m)
    print(m.group())
    print(m.start())
    print(m.end())
    print(m.span())
    
# result
---------------
<re.Match object; span=(11, 16), match='$5.94'>
$5.94
11
16
(11, 16)
---------------
<re.Match object; span=(44, 49), match='$7.46'>
$7.46
44
49
(44, 49)

5. Sub

특정 패턴을 갖는 문자열을 찾은 다음, 다른 문자로 바꾸고 싶다면 sub을 쓰면 된다.

s = 'With Deal:	$5.94 + No Import Fees Deposit & $7.46 Shipping to Korea, Republic of'
price_pattern = re.sub('\$\d[.]\d{2}','$TBD', s)
print(price_pattern)

# result
With Deal:	$TBD + No Import Fees Deposit & $TBD Shipping to Korea, Republic of

Categories:

Updated: