728x90
반응형

이전 포스트에서 정규 표현식이 무엇인지, 메타 문자가 무엇인지에 대해 가볍게 알아보았다. 앞서 학습한 메타 문자를 예제를 기반으로 제대로 다뤄보려면, 자신이 만든 정규 표현식 패턴이 제대로 만들어졌는지를 확인할 필요가 있다.

이번 포스트에서는 Python에서 정규 표현식을 어떻게 지원하는지 re 모듈을 통해 알아보도록 하겠다. 이번 포스트에서 예제로 정규 표현식으로 만든 패턴을 일부 사용하긴 할 것이나, 이는 다음 포스트에서 자세히 다룰 것이므로 이번에는 참고만 하도록 하자.

 

 

 

 

1. Python과 re 모듈

정규 표현식으로 만든 패턴 문자열이 제대로 작동하는지 알기 위해선 Python에서 기본적으로 제공하는 re 모듈에 대해 알 필요가 있다. 

  • re 모듈은 Python에서 정규 표현식을 지원하기 위해 제공하는 기본 라이브러리이다.
  • re 모듈은 컴파일러 객체를 선언하고, 다음과 같은 메서드를 통해 작동한다.
# re 모듈을 가지고 온다.
import re
pattern = "cat"

regex_pattern = re.compile(pattern)
  • re 모듈의 컴파일 객체는 다음과 같은 메서드를 통해 작동한다.
  • 컴파일 객체는 regex_pattern이 아닌 다른 변수명에 담아도 상관없다.
Method/Attribute 기능
match() 문자열의 왼쪽(시작)부터 컴파일된 정규 표현식 패턴 객체가 일치하는지 판단한다.
search() 문자열 전체에서 정규 표현식 패턴 객체와 일치하는 첫 문자의 위치를 찾는다.
findall() 정규 표현식 패턴 객체와 일치하는 모든 문자열을 찾아 리스트로 반환한다.
finditer() 정규 표현식 패턴 객체와 일치하는 모든 문자열을 찾아 이터레이터(Iterator)로 반환한다.

 

 

 

 

1.1. match()

  • match 함수는 왼쪽(시작)부터 정규 표현식 패턴이 일치하는 곳까지를 반환해준다.
  • 왼쪽부터라는 것이 무슨 뜻인지 아리송한 감이 있는데, 이게 정확히 어떻게 돌아가는지 알아보도록 하자.
  • 다음과 같은 정규 표현식 패턴과 Text_data가 있다고 가정해보자.
>>> pattern = "cats"

>>> regex_pattern = re.compile(pattern)

>>> Text_data = "I love cats and I wish I had cats."
  • Text_data에는 pattern인 cats가 들어 있으므로, 문제없이 찾을 수 있을 것 같다.
  • 이를 match 메서드를 이용해서 탐색해보자.
>>> m = regex_pattern.match(Text_data)
>>> print(m)
None
  • 분명히 "cats"이라는 단어가 Text_data 안에 있음에도 탐색되지 않았다.
  • 이는 match() 메서드가 왼쪽에서부터 탐색하기 때문으로, 주어진 "cats"라는 pattern은 Text_data의 문장 중간에 존재하지, 앞에서부터는 존재하지 않기 때문이다.
  • "cats"가 나오도록 패턴을 조금 수정해보자.
    • 새로운 패턴은 다음 포스트에서 자세히 다룰 것이므로, 일단 따라가 보자!
>>> pattern = ".*cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.match(Text_data)
>>> print(m)
<_sre.SRE_Match object; span=(0, 33), match='I love cats and I wish I had cats'>
  • pattern으로 '.*cats'를 사용하였는데, '.*'는 cats 앞에 문자가 0개 이상 존재한다는 의미다.
  • 즉, 0개 이상 문자가 앞에 존재하는 cats와 일치하는 문장을 탐색한 것이다.
  • match 메서드는 _sre.SRE_Match 객체를 리턴한다.
  • _sre.SRE_Match 객체는 Match 된 문자열의 위치, Match 된 문자열을 반환한다.
  • 이를 통해, Match 메서드 시작부터 패턴이 일치하는지 판단한다는 것을 알 수 있다.

 

 

  • 이번에는 pattern을 아주 조금만 수정해서 match() 메서드의 성질을 보다 자세히 보자.
>>> pattern = ".*cats "
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.match(Text_data)
>>> print(m)
<_sre.SRE_Match object; span=(0, 12), match='I love cats '>
  • 앞서 사용한 pattern 뒤에 스페이스(space)를 추가하자, "I love cats "까지만 출력되었다.
  • 즉, match 메서드는 전체 문장이 얼마나 길든 간에 처음부터 pattern과 일치되는 곳까지만 반환하는 것을 알 수 있다.

 

 

1.1.1. match 객체가 제공하는 메서드

  • 앞서 match는 _sre.SRE_Match 객체를 반환한다고 하였는데, 이는 다음과 같은 메서드를 이용해 내부 정보에 접근할 수 있다.
Method / Attribute 기능
group() 매치된 문자열 출력
start() 매치된 문자열의 시작 지점 출력
end() 매치된 문자열의 끝 지점 출력
span() 매치된 문자열의 시작 지점과 끝 지점을 튜플로 출력
  • 예제를 통해 보도록 하자.
>>> pattern = ".*cats "
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.match(Text_data)

>>> print(m.group())
>>> print(m.start())
>>> print(m.end())
>>> print(m.span())
I love cats 
0
12
(0, 12)

 

 

 

 

1.2. search()

  • search 메서드는 문자열 전체에서 정규 표현식 패턴 객체와 일치하는 첫 문자의 위치를 반환한다.
  • 앞서 만든 예제에 그대로 적용해보도록 하자.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.search(Text_data)
>>> print(m)
<_sre.SRE_Match object; span=(7, 11), match='cats'>
  • search 메서드도 match 메서드와 동일하게 _sre.SRE_Match 객체를 반환한다.
  • search 메서드는 match 메서드와 달리 pattern이 처음부터 일치할 필요는 없으며, Text_data 내 가장 먼저 일치하는 문자열의 위치와 문자열을 반환한다.
  • 만약 존재하지 않는 문자열을 검색하면, match와 마찬가지로 None을 반환한다.
>>> pattern = "dogs"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.search(Text_data)
>>> print(m)
None
  • search() 메서드는 match() 메서드와 동일한 _sre.SRE_Match 객체를 반환하므로, match와 동일한 방법을 통해, 원하는 값을 출력할 수 있다.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.search(Text_data)
>>> print(m.group())
>>> print(m.start())
>>> print(m.end())
>>> print(m.span())
cats
7
11
(7, 11)

 

 

 

 

1.3. findall()

  • findall() 메서드는 가장 설명이 깔끔하면서, 유용한 기능을 제공하는 메서드다.
  • 말 그대로, 문장 안에 원하는 텍스트가 존재하면 그 텍스트를 리스트(List)로 모두 가지고 오는 기능으로, 이를 이용해서 해당 문장 안에 패턴에 일치하는 문자열이 몇 개 존재하는지도 쉽게 확인할 수 있다.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.findall(Text_data)
>>> print(m)
['cats', 'cats']
  • match, search와 달리 리스트를 바로 반환하므로, group, start, end, span과 같은 함수를 사용하지 않는다.

 

 

 

 

1.4. finditer()

  • finditer()는 findall()의 진화 버전이라고 할 수 있는 메서드로, Python 초보자라면 그 기능을 알기 쉽지 않다.
  • finditer()와 findall()의 차이는 결과를 이터레이터(Iterator)로 출력할 것인지 리스트(List)로 출력할 것인지로, List로 출력하면, 사용하기는 단순하나 단지 패턴에 일치하는 단어들을 List에 담아 출력하므로, 그 기능이 한정적이다.
  • 그러나 finditer()는 이터레이터를 통해 값을 반환하므로, 더 많은 정보를 담을 수 있고, 엄청나게 큰 Text_data가 대상일 때, 메모리 측면에서도 훨씬 유리하다.
  • 이터레이터에 대해서는 나중에 자세히 다룰 것이므로, 간단하게 내가 원할 때, 순서대로 값을 반환할 수 있는 녀석이라고 생각하면 된다.
  • 이터레이터는 모든 데이터를 메모리에 올려놓지 않기 때문에 findall()보다 용량을 덜 차지한다는 장점이 있다.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.finditer(Text_data)
>>> print(m)
<callable_iterator object at 0x000001EB3D932358>
  • 출력된 결과가 앞서 봤던 match(), search(), findall()과 다르게 전혀 알아볼 수 없는 녀석이 나왔다.
  • Python의 이터레이터는 next() 또는 for와 같은 함수를 이용해야 값을 출력할 수 있다.
# 첫 번째 next
>>> next(m)
<_sre.SRE_Match object; span=(7, 11), match='cats'>

# 두 번째 next
>>> next(m)
<_sre.SRE_Match object; span=(29, 33), match='cats'>

# 세 번째 next
>>> next(m)

  • next()로 이터레이터 안에 있는 값을 꺼내자 _sre.SRE_Match 객체가 반환되는 것을 볼 수 있다.
  • 이터레이터 안에는 2개의 객체만 존재하므로, 마지막 객체 반환 시, StopIteration을 출력하는 것을 볼 수 있다.
  • 이번에는 for문으로 이터레이터 안의 객체를 꺼내보자.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.finditer(Text_data)

>>> for i in m: 
>>>    print(i)
    
<_sre.SRE_Match object; span=(7, 11), match='cats'>
<_sre.SRE_Match object; span=(29, 33), match='cats'>
  • finditer() 메서드는 findall() 메서드와 달리 _sre.SRE_Match 객체를 반환하므로, 문장 내 패턴에 일치하는 대상들의 위치 정보도 쉽게 반환할 수 있다.
>>> pattern = "cats"
>>> regex_pattern = re.compile(pattern)
>>> Text_data = "I love cats and I wish I had cats."

>>> m = regex_pattern.finditer(Text_data)

>>> for i in m:
>>>    print(i.span())

(7, 11)
(29, 33)

 

 

 

 

이번 포스트에서는 Python에서 기본적으로 제공하는 re 모듈에 대해 한 번 다뤄봤다. 텍스트 데이터를 다룰 때, re 모듈은 매우 유용하게 사용되므로 꼭 숙지하도록 하자.

다음 포스트에서는 메타 문자를 이용하여, 예제 데이터에 대한 정규 표현식 패턴을 만들어보고, re 모듈로 이를 검증해보도록 하자.

728x90
반응형

+ Recent posts