안녕하세요! 그 동안의 포스트(튜플, 뉴스 클러스터링)에서 간간히 설명드렸던 정규표현식에 대해 알아보는 시간을 가지겠습니다. 언젠가 꼭 한 번은 정규표현식에 대해 내용들을 정리를 하고 싶었는데 감회가 새롭네요. 😀 덕분에 새롭게 알게 된 부분들도 있었습니다. 방대한 양을 이번에 모두 다루지는 못하겠지만, 중요한 내용들을 중점적으로 설명드리도록 하겠습니다. 이번 시간에는 정규표현식 문자열을 다루는 데에 있어서 정말 정말 중요한 패키지인 re에 대해 알아보도록 하겠습니다!

re 패키지 함수

정규표현식(Regular Expression)에 대해 알아보기 전에 유용한 문자열 패키지인 re에 대해 알아보겠습니다. 저도 문자열을 처리할 때 re.sub과 같은 함수를 많이 이용하곤 합니다. 이 외에도 다양한 기능의 함수들로 구성되어 있습니다.


  • re.compile() : 정규표현식을 취합
import re
c = re.compile('[a-z]+')

뒤에 설명하겠지만, ‘[a-z]+’는 소문자 알파벳이 1개 이상인 문자를 말합니다. 즉, 위의 코드는 ‘[a-z]+’인 문자를 취합한다는 의미이고, 이를 c라고 두어 다양한 기능의 re 함수를 적용할 수 있습니다.


  • re.match() : 문자열의 처음부터 정규표현식과 매치되는지 조사
c = re.compile('[a-z]+')
a = c.match("algorithm")
print(a)
--------------------------------------------------------------------------------------------------------------------------------
<re.Match object; span=(0, 9), match='algorithm'>

위에서 c라는 이름으로 compile한 정규표현식을 “algorithm” 문자열에 match를 시킵니다. “algorithm”은 모두 소문자 알파벳으로 이루어진 문자열입니다. 따라서 match가 가능하고, match 객체가 결과로 나옵니다.

c = re.compile('[a-z]+')
a = c.match("2 algorithm")
print(a)
--------------------------------------------------------------------------------------------------------------------------------
None

“2 algorithm” 문자열에는 소문자 알파벳 뿐만 아니라 숫자도 포함되어 있습니다. 따라서 c와 match가 불가능하여 None으로 결과가 나옵니다.

a = re.match('[a-z]+', "algorithm")
print(a)
--------------------------------------------------------------------------------------------------------------------------------
None

위와 같이 re.match를 사용함으로써 re.complie()을 생략하여 코드를 축약시킬 수 있습니다. 그러나 한 번 만든 compile 패턴 객체를 여러 번 계속 사용해야 한다면 위의 코드보다는 re.complie()을 이용하는 편이 좋습니다.


  • re.search() : 문자열 전체를 검색하여 정규식과 매치되는지 조사
c = re.compile('[a-z]+')
a = c.match("algorithm")
print(a)
--------------------------------------------------------------------------------------------------------------------------------
<re.Match object; span=(0, 9), match='algorithm'>

위의 코드의 결과를 보았을 때, re.search()는 re.match()와 크게 다른 점이 없는 함수처럼 보입니다.

c = re.compile('[a-z]+')
a = c.search("2 algorithm")
print(a)
--------------------------------------------------------------------------------------------------------------------------------
<re.Match object; span=(2, 11), match='algorithm'>

하지만 re.match()와 다르게 re.search()는 “2 algorithm” 문자열에 대해 None이 아닌 결과가 나옵니다. re.search()는 문자열 처음부터 검색을 하는 것이 아니라 문자열 전체를 검색하기 때문에 숫자인 “2” 이후의 문자열인 “algorithm”가 매칭됩니다. 결론적으로 re.match()와 re.search()는 문자열의 처음부터 검색 여부에 따라 다르게 사용됩니다!


  • re.findall() : 정규표현식과 매치되는 모든 값을 찾아 리스트로 추출
c = re.compile('[a-z]+')
answer = c.findall("my algorithm is so good")
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
['my', 'algorithm', 'is', 'so', 'good']


  • re.finditer() : 정규표현식과 매치되는 반복 가능한 객체를 추출
c = re.compile('[a-z]+')
answer = c.finditer("my algorithm is so good")
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
<callable_iterator object at 0x000001EFE078B0D0>

re.finditer()는 리스트가 추출되는 re.findall()과 다르게 알 수 없는 문자들로 이루어진 결과값이 나옵니다.

for ans in answer :
    print(ans)
--------------------------------------------------------------------------------------------------------------------------------
<re.Match object; span=(0, 2), match='my'>
<re.Match object; span=(3, 12), match='algorithm'>
<re.Match object; span=(13, 15), match='is'>
<re.Match object; span=(16, 18), match='so'>
<re.Match object; span=(19, 23), match='good'>

나온 결과값에 대해 for문을 이용하여 요소들을 print해보면 위와 같이 각각의 match 객체들로 결과가 나오게 됩니다.


  • re.sub() : 정규표현식과 매치되는 모든 값을 찾아 다른 문자열로 변환
c = re.compile('[a-z]+')
answer = c.sub("곰", "a b c 1 2 3 A B C")
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
   1 2 3 A B C

위의 코드는 “a b c 1 2 3 A B C” 문자열에 대해 알파벳 소문자를 “곰” 문자로 바꿉니다. re.sub()에는 두 개의 인수가 필요한데 첫 번째 인수는 바꿀 문자열, 두 번째 인수는 대상 문자열을 나타냅니다.

c = re.compile('[a-z]+')
answer = c.sub("곰", "a b c 1 2 3 A B C", count = 1)
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
 b c 1 2 3 A B C

re.sub()에 count 인수를 넣으면 처음부터 일치하는 문자열 중 몇 개까지 문자열을 바꿀지 정할 수 있습니다.

answer = re.sub('[a-z]+', "곰", "a b c 1 2 3 A B C")
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
   1 2 3 A B C

re.compile() 함수가 없더라도, re.sub()에 직접 매칭할 정규표현식을 기입하면 됩니다.


  • re.subn() : 정규표현식과 매치되는 모든 값을 찾아 다른 문자열로 변환한 결과를 튜플로 추출
c = re.compile('[a-z]+')
answer = c.subn("곰", "a b c 1 2 3 A B C")
print(answer)
--------------------------------------------------------------------------------------------------------------------------------
('곰 곰 곰 1 2 3 A B C', 3)

매칭된 문자열을 다른 문자열로 변환하는 점이 re.sub()과 비슷합니다. 하지만 변경된 문자열과 문자열 변환이 발생한 횟수가 튜플로서 추출된다는 점이 다른 점이라고 할 수 있습니다.


match 객체의 메서드

re.match(), re.search() 등의 결과에 아래와 같은 메서드를 이용하면 매칭된 문자열, 위치 등을 알 수 있다.

  • group() : 매칭된 문자열을 추출

  • start() : 매칭된 문자열의 시작 위치를 추출

  • end() : 매칭된 문자열의 끝 위치를 추출

  • span() : 매칭된 문자열의 시작, 끝 위치를 튜플로 추출

c = re.compile('[a-z]+')
a = c.match("algorithm")
print(a.group())
print(a.start())
print(a.end())
print(a.span())
--------------------------------------------------------------------------------------------------------------------------------
algorithm
0
9
(0, 9)

group()을 통해 “algorithm”을 추출하고, start(), end(), span()을 통해 해당 문자열의 시작, 끝 위치를 추출합니다.

re.search()의 결과도 동일하게 나옵니다.

c = re.compile('[a-z]+')
a = c.search("2 algorithm")
print(a.group())
print(a.start())
print(a.end())
print(a.span())
--------------------------------------------------------------------------------------------------------------------------------
algorithm
2
11
(2, 11)


다음 포스팅으로 정규표현식의 메타 문자에 대해서 설명드리도록 하겠습니다.


Reference

  1. 점프 투 파이썬 08-2
  2. 점프 투 파이썬 08-3


DATA_100%_LOGO_LIGHT



댓글남기기