09-4 정규식으로 문자열 처리에 날개 달기
정규식이란?
수만 개의 문자열 중에서 내가 원하는 패턴의 문자열만 추출하려면 어떻게 해야 할까? 예를 들어 I like apple, I like to make application이라는 문자열에서 app을 포함하는 문자열만 추출하려면 어떻게 해야 할까? find 메서드를 사용해도 되지만 이런 경우에는 정규식을 사용하면 더 편리하다. 그러면 정규식이 무엇인지 실습을 통해 자세히 알아보자.
정규식 표현 - 문법, 특수 문자¶
정규식 표현에 사용되는 문법과 특수 문자를 다음과 같이 표로 정리했다. 정규식을 사용하려면 다음 표를 참고하여 찾고자 하는 문자열의 정규식 패턴을 만들어야 한다. https://regex101.com 사이트에서 정규식 패턴을 실습해 보는 것을 추천한다. 이 사이트는 정규식 패턴의 결과를 바로 볼 수 있어 실습에 많은 도움이 될 것이다. 아래 표를 참고하여 실습 코드를 작성하고 결과를 확인해보자.¶
기본 정규식 문법¶
문법 | 실습 코드 | 설명 |
---|---|---|
. | .a | 문자(a) 앞에 문자 1개가 있는 패턴을 찾는다. |
^ | ^I like | 문자열의 처음부터 일치하는 패턴을 찾는다. |
$ | on$ | 문자열의 끝 부분부터 일치하는 패턴을 찾는다. |
* | n\d* | n 이후 숫자(\d)가 0개 이상인 패턴을 찾는다. |
+ | n\d+ | n 이후 숫자(\d)가 1개 이상인 패턴을 찾는다. |
? | apple? | ?의 앞의 문자(e)가 있거나 없는 패턴을 찾는다. |
{m} | n\d{2} | n 이후 숫자(\d)가 2개 {(2})인 패턴을 찾는다. |
{m, n} | n\d{2, 4} | n 이후 숫자(\d)가 2개({2}) 이상, 2개({4})이하인 패턴을 찾는다. |
\ | *, \?, + | *, ?, +와 같은 특수 문자를 검색할 때 이스케이프 문자()를 사용한다. |
[] | [cfh]all | c, f, h 중 1개를 포함하고 나머지 문자열이 all인 패턴을 찾는다. |
| | apple|application | apple이나 application 중 하나만 있는 패턴을 찾는다(OR 연산). |
() | (\d+)-(\d+)-(\d+) | ()에 지정한 패턴을 찾을 때 사용한다. |
정규식 특수 문자¶
특수 문자 | 설명 |
---|---|
\d | 숫자 1개를 의미한다([0-9]와 동일). |
\D | 숫자 이외의 문자 1개를 의미한다([^0-9]와 동일) |
\s | 공백이나 탭 1개를 의미한다. |
\S | 공백 문자 이외의 문자 1개를 의미한다. |
\w | 알파벳 1개를 의미한다. |
\W | 알파벳 이외의 문자 1개를 의미한다(한글, 중국어 등). |
정규식 표현 - 메서드¶
다음은 정규식 메서드이다. 정규식 메서드는 정규식 패턴을 사용하지 않아도 메서드를 호출하는 방법으로 원하는 패턴의 문자열을 찾을 수 있도록 해준다.
정규식 메서드¶
함수 | 실습 코드 | 설명 |
---|---|---|
search |
m = re.search('[0-9]{4}', test)) |
첫 번째로 찾은 패턴의 양 끝 인덱스를 반환한다. |
match |
m = re.match('[0-9]{4}', test)) |
문자열의 처음부터 검색하여 찾아낸 패턴의 양 끝 인덱스를 반환한다. |
fullmatch |
m = re.fullmatch('\d+\s\d+\s\d+\s\d+', test) |
전체 문자열이 일치하는지 검사한다. |
split |
m = re.split('\s', test) |
지정한 패턴으로 잘라낸 문자열을 리스트로 반환한다. |
findall |
m = re.findall('[0-9]{4}', test) |
n 이후 숫자(\d)가 1개 이상인 패턴을 찾는다. |
finditer |
m = re.finditer('[0-9]{4}, test') |
findall 메서드와 기능이 동일하지만 iterator를 반환한다. |
sub |
print(re.sub('\s', '-', test)) |
첫 번째 인자로 전달한 값(패턴)을 두 번째 인자로 전달한 값(easyspub)으로 교체한다. |
정규식으로 전화번호 패턴 찾기¶
전화번호와 같은 단순한 데이터도 복잡하고 다양한 정규식이 필요하다. 처음에는 정규식이 복잡하고 어려워 보여 부담스러울 수 있다. 하지만 정규식은 원하는 패턴의 문자열을 가장 효율적으로 찾아주는 방법이다. 따라서 데이터 분석이 필요한 분야에서는 정규식을 알아두면 좋다.
다음과 같이 re 모듈과 테스트용 문자열을 준비한다.¶
import re
tele_num = '1234567890'
match 메서드를 사용하여 길이가 10인 숫자를 확인해 보겠다. pattern 인자에는 10개의 숫자를 의미하는 10개의 \d, string에는 테스트용 문자열은 tele_num을 전달했습니다. 만약 패턴을 찾으면 Match 오브젝트를 반환한다. Match 오브젝트를 출력하면 span에는 찾은 패턴의 인덱스가, match에는 찾은 패턴의 문자열이 있는 것을 확인할 수 있다.¶
m = re.match(pattern='\d\d\d\d\d\d\d\d\d\d', string=tele_num)
print(type(m))
<class 're.Match'>
print(m)
<re.Match object; span=(0, 10), match='1234567890'>
이때 bool 메서드에 m을 전달하면 True나 False를 얻을 수 있다. 즉, match 메서드가 반환한 Match 오브젝트는 bool 메서드로 True, False를 판단할 수 있다.¶
print(bool(m))
True
if m:
print('match')
else:
print('no match')
match
Match 오브젝트에는 찾아낸 패턴의 정보를 확인할 수 있는 다양한 메서드가 있다. start와 end 메서드는 첫 번째와 마지막 인덱스를 반환한다. span 메서드는 찾은 패턴의 첫번째와 마지막 인덱스를 한 번에 반환한다. group 메서드는 찾아낸 패턴을 반환한다.¶
print(m.start())
0
print(m.end())
10
print(m.span())
(0, 10)
print(m.group())
1234567890
그런데 전화번호를 입력하는 방법은 1234567890이 아니라 123-456-7890이나 123 456 7890과 같은 방법도 있다. 다음은 앞에서 사용한 패턴을 그대로 적용하여 123 456 7890을 검사한 것이다. 다음은 앞에서 사용한 패턴을 그대로 적용하여 123 456 7890을 검사한 것이다. 그러면 패턴을 찾지 못해 Match 오브젝트가 아닌 None을 출력한다.¶
참고: \d\d\d\d\d\d\d\d\d\d와 d{10}은 같은 표현이다.
tele_num_spaces = '123 456 7890'
m = re.match(pattern='\d{10}', string=tele_num_spaces)
print(m)
None
if m:
print('match')
else:
print('no match')
no match
과정 5의 문제를 해결하려면 정규식을 다시 작성해야 한다. 다음과 같이 빈 칸을 의미하는 정규식 \s?를 넣어 패턴을 다시 만들었다.¶
p = '\d{3}\s?\d{3}\s?\d{4}'
m = re.match(pattern=p, string=tele_num_spaces)
print(m)
<re.Match object; span=(0, 12), match='123 456 7890'>
지역 코드는 소괄호로 감싸고 나머지 번호는 반각 기호로 구분한 전화번호의 정규식은 다음과 같이 작성한다.¶
tele_num_space_paren_dash = '(123) 456-7890'
p = '\(?\d{3}\)?\s?\d{3}\s?-?\d{4}'
m = re.match(pattern=p, string=tele_num_space_paren_dash)
print(m)
<re.Match object; span=(0, 14), match='(123) 456-7890'>
국가 코드까지 있는 전화번호의 정규식은 다음과 같이 작성한다.¶
cnty_tele_num_space_paren_dash = '+1 (123) 456-7890'
p = '\+?1\s?\(?\d{3}\)?\s?\d{3}\s?-?\d{4}'
m = re.match(pattern=p, string=cnty_tele_num_space_paren_dash)
print(m)
<re.Match object; span=(0, 17), match='+1 (123) 456-7890'>
compile 메서드로 정규식 메서드 사용하기
패턴을 반복해서 사용하려면 compile 메서드로 패턴을 컴파일한 다음 변수에 저장하여 사용하면 된다. 다음은 앞에서 실습한 내용 중 하나를 compile 메서드로 처리한 것이다. 패턴을 컴파일한 다음 변수에 저장했기 때문에 정규식 메서드를 반복해서 사용할 수 있어 매우 편리하다.
p = re.compile('\d{10}')
s = '1234567890'
m = p.match(s)
print(s)
1234567890
마무리하며
세상에는 문자열로 이루어진 데이터가 너무나도 많다. 그래서 데이터 과학자들은 문자열을 처리하는 방법을 매우 중요하게 생각한다. 그래서 09장에서는 판다스와 전혀 상관없어 보이는 문자열 처리를 설명한 것이다. 다음 장으로 넘어가기 전에 반드시 09장의 예제를 한 번씩 실습해 보기 바란다.
출처 : Do it! 데이터 분석을 위한 판다스
'Python > Pandas' 카테고리의 다른 글
<파이썬 판다스> Chapter 10-2 apply 메서드 활용하기 - 기초 (0) | 2023.05.14 |
---|---|
<파이썬 판다스> Chapter 10-1 간단한 함수 만들기 (0) | 2023.05.14 |
<파이썬 판다스> Chapter 09-3 문자열 포매팅 (0) | 2023.05.09 |
<파이썬 판다스> Chapter 09-2 문자열 메서드 (0) | 2023.05.08 |
<파이썬 판다스> Chapter 09-1 문자열 다루기 (0) | 2023.05.07 |