[Python] re모듈, 정규표현식(regular expression)

업데이트:

정규표현식

정규표현식은 특정 문자열이나 문장 안에서 나만의 규칙을 만들어 어떤 단어나 값들을 식별하고 싶을때 사용되는 표현식이다.
파이썬에서는 이러한 정규표현식을 지원해주는 re모듈이 존재한다.

1. 정규표현식 생성 방식

정규표현식을 사용하는 방법은 크게 두가지가 있다.

정규표현식을 미리 컴파일 해서 패턴을 저장해놓는 경우와, 함수를 사용할때 마다 직접 넣어주는 경우가 있다.

1-1. re.compile() : 패턴저장

아래와 같이 미리 패턴을 컴파일 해두고, 이 객체가 가지는 함수를 보자.

import re
pattern1 = re.compile("\d{2}")
print(type(pattern1))
print('\n')
print(dir(pattern1))
[Output]
<class 're.Pattern'>

[...
 'findall',
 'finditer',
 'flags',
 'fullmatch',
 'groupindex',
 'groups',
 'match',
 'pattern',
 'scanner',
 'search',
 'split',
 'sub',
 'subn']

컴파일된 객체는 re.Pattern이라는 타입으로 저장되고, 아직 설명하지 않았지만 컴파일된 객체에 바로 함수를 적용해 사용하면 된다.

pattern1.findall("dflds78flksd")
[Output]
['78']

장점은 다음과 같다.

  • 컴파일(compile)을 활용하는 경우, 한 줄 더 써야 하지만 패턴 객체(p)를 만들고 나서 여러번 재사용이 가능하다.
  • 반복적인 매칭 작업이 필요할 경우에는 패턴을 미리 컴파일해서 시간을 단축할 수 있다.

1-2. 축약형

패턴을 컴파일하지 않고 그냥 바로 적용해도 된다.

re.findall("\d{2}","dflds78flksd")
[Output]
['78']

같은 결과이다.

반복작업 없이 한번 정도 사용하고자 할때 축약형으로 쓰는게 더 편할 것 같다.

2. 메타 문자

정규표현식을 만들때 메타문자의 이해가 정말 중요하다.

메타 문자 설명
[] 문자 클래스
. \n을 제외한 모든 문자와 매치(점 하나는 글자 하나를 의미)
* 0회 이상 반복(생략 가능)
+ 1회 이상 반복
{m,n} m회 이상 n회 이하 반복
l or 조건식을 의미
^ 문자열의 시작 의미
$ 문자열의 끝을 의미
? 0회 이상 1회 이하(0또는 1)
\ 이스케이프, 또는 메타 문자를 일반 문자로 인식하게 함
() 그룹핑, 추출할 패턴 지정

2-1. [] 문자클래스

세 가지 특징을 기억하자.

  • 대괄호 안에는 어떤 것이든 들어갈 수 있다.(대소문자 구별)
  • 안에 들어간 것 끼리는 or로 연결(하이폰 - 으로 연결)

예를 들어 [a-z]는 a~z까지의 문자를 의미하고, [0-9]는 0~9까지 숫자,
만약 [abc]라면 ‘a’,’b’,’c’ 중에서 한개의 문자와 매칭된다.

re.findall("[a]", "yganalyst")
[Output]
['a', 'a']
re.findall("[ant]", "yganalyst")
[Output]
['a', 'n', 'a', 't']
re.findall("[a-z]", "yganalyst")
[Output]
['y', 'g', 'a', 'n', 'a', 'l', 'y', 's', 't']

2-2. 문자클래스 축약표현

앞에서 메타문자 []만으로 일일이 정규표현식을 만들기에는 한계가 있다.
따라서 이를 축약한 표현을 알아두는게 매우 중요하다.

png 출처 : https://whatisthenext.tistory.com/116

축약표현 설명 사용처
\d 숫자를 찾음 숫자
\D 숫자가 아닌 것을 찾음 텍스트+특수문자+whitesace
\s whitespace 문자인 것을 찾음 스페이스, 탭, 개행(new line)
\S whitespace 문자가 아닌 것을 찾음 텍스트+특수문자+숫자
\w 문자+숫자인 것을 찾음(특수문자 제외, _포함) 텍스트+숫자
\W 문자+숫자가 아닌 것을 찾음 특수문자+공백

앞에서 메타문자 []를 사용한 방식 대신 축약표현을 이용해보자.

re.findall("\w", "yganalyst")
[Output]
['y', 'g', 'a', 'n', 'a', 'l', 'y', 's', 't']

2-3. () : 그룹핑

그룹핑을 한번 짚고 넙어가자.

p = re.compile("(\w*)\s+(\w{2}-\w*)")
m = p.search("my name is yg-analyst")

print(m.group(0))
print(m.group(1))
print(m.group(2))
[Output]
is yg-analyst
is
yg-analyst

설명하자면 위 정규표현식에 ()를 두개 넣어 두 그룹으로 만들었다.
이렇게 그룹핑을 한 정규표현식을 search하는 순서는 다음과 같다.

  1. ()고려하지 않고 해당 정규표현식으로 우선 수색 => group(0)에 저장
  2. group(0)에 저장된 표현중 첫번째 ()에 해당하는 문자열 => group(1)에 저장
  3. group(0)에 저장된 표현중 두번째 ()에 해당하는 문자열 => group(2)에 저장

3. 메서드

이제 정규표현식을 잘 만들었으면, 적절한 메서드를 적용하는 것도 중요하다.

메서드 설명
match 문자열의 처음부터 정규식과 매치되는지 조사
search 문자열 전체를 검색하여 정규식과 매치되는지 조사
findall 정규식과 매치되는 모든 문자열(substring)을 리스트로 리턴
split 패턴으로 나누기
sub 패턴 대체하기

3-1. search()match()

match()는 처음이 일치하지 않으면 None을 반환
search()는 처음이 일치하지 않더라도 전체를 수색해봄

source = "data makes future"
match = re.match("makse", source)
search = re.search("makes", source)
 
if match:
    print("match : ", match.group())
 
if search:
    print("search : ", search.group())
[Output]
search :  makes

또 하나의 특징은 두 함수 모두 원하는 것이 발견되면 수색을 멈춘다.

group()은 앞의 그룹핑에서 설명했던 group(0)과 같은 의미이다.

3-2. findall()finditer()

findall()은 앞에서도 계속 사용해서 알겠듯이 정규식과 매칭되는 모든 문자열을 리스트 형식으로 리턴해준다.
finditer()도 마찬가지로 매칭되는 모든문자를 찾되, iterator객체로 반환한다.

p = re.compile('[a-z]+') # (a-z)가 1회 이상 반복 
m1 = p.findall("Life is to short")
m2 = p.finditer("Life is to short")

print(m1)
print(type(m1))
print('\n')
print(m2)
print(type(m2))
[Output]
['ife', 'is', 'to', 'short']
<class 'list'>


<callable_iterator object at 0x000001527E2DA9E8>
<class 'callable_iterator'>

iterator객체를 확인하려면 다시,

for i in m2:
    if i:
        print(i.group())
[Output]
ife
is
to
short

이렇게 해줘야한다.

4. 정규표현식, r''의 의미

정규패턴식 앞에 r이 붙어 있는 경우가 많다. 파이썬 정규식에는 Raw string이라고 해서, 컴파일 해야 하는 정규식이 Raw String(순수한 문자)임을 알려줄 수 있도록 문법이 있다.

만약p = re.compile('\section') 이라고 쓴다면 \s는 공백문자를 의미하는 [ \t\n\r\f\v]이 되어버려서 원하는 결과를 찾지 못한다. 그래서 #10번에서 배웠듯이 이스케이프 \를 활용해 \section이라고 해주면 되지만, 파이썬은 특수하게 r을 사용하면 백슬래쉬를 1개만 써도 두개를 쓴 것과 같은 효과를 갖는다.

Reference

여기를 보고 공부했으며, 많은 도움을 받았습니다. 감사합니다.

댓글남기기