[Python] 정규 표현식(2)

4 분 소요


👩 re : 파이썬 정규식 지원 모듈

  • re : regular expression 약자
  • 사용법 : 파이썬을 설치할 때 자동으로 설치되므로 import만 진행하면 된다.
    import re
    


👩 문자열 검색 메소드 4가지

⬛ match() : 문자열 첫 부분부터 정규식과 매치되는지 조사

>>> import re
>>> print(re.match('[a-z]+', 'ab'))
>>> print(re.match('[a-z]+', '123'))

<re.Match object; span=(0, 2), match='ab'>
None
  • 정규식과 매치될 경우 match 객체를 돌려주고, 매치되지 않을 경우 None을 돌려준다.


⬛ search() : 문자열 전체에서 정규식과 매치되는지 조사

>>> import re
>>> print(re.search('[a-z]+', 'ab'))
>>> print(re.search('[a-z]+', '123cc'))

<re.Match object; span=(0, 2), match='ab'>
<re.Match object; span=(3, 5), match='cc'>
  • 정규식과 매치될 경우 match 객체를 돌려주고, 매치되지 않을 경우 None을 돌려준다.
  • ‘123cc’의 경우, 문자열 전체를 검색하기 때문에 “cc” 문자열이 정규식과 매치되게 된다.


⬛ findall() : 정규식과 매치되는 모든 문자열을 리스트 형태로 돌려준다.

>>> import re
>>> print(re.findall('[a-zA-z]+', 'Have a nice day!'))

['Have', 'a', 'nice', 'day']


⬛ finditer() : 정규식과 매치되는 모든 문자열을 반복 가능한 객체 형태로 돌려준다.

>>> import re
>>> matches = re.finditer('[a-zA-z]+', 'Have a nice day!')
>>> print(matches)

<callable_iterator object at 0x7f1d82b2c4f0>

>>> for match in matches: print(match)

<re.Match object; span=(0, 4), match='Have'>
<re.Match object; span=(5, 6), match='a'>
<re.Match object; span=(7, 11), match='nice'>
<re.Match object; span=(12, 15), match='day'>
  • findall과 동일하지만 결과로 반복 가능한 객체를 리턴한다. 반복 가능한 객체의 각 요소는 match 객체이다.


👩 match 객체에 대한 메소드

메소드 역할
group() 정규식과 매치된 문자열 리턴
start() 매치된 문자열의 시작 위치 리턴
end() 매치된 문자열의 위치 리턴
span() 매치된 문자열의 (시작, 끝) 튜플 리턴

🙄 그렇다면 예를 한 번 살펴보자!!

>>> import re
>>> m = re.search('[a-zA-z]+', '123hello456')
>>> print(m.group())
>>> print(m.start())
>>> print(m.end())
>>> print(m.span())

hello
3
8
(3, 8)
  • match()의 경우 첫 부분부터 검색을 시작하므로 start()의 값이 항상 0이다.


👩 complie() : 하나의 정규식으로 여러 작업 수행하기!

  • 하나의 정규식을 여러 번 사용하고자 할 경우, compile()로 리턴된 객체를 사용해 여러 작업을 수행할 수 있다.
>>> import re
>>> m = re.compile('[a-zA-z0-9]')
>>> print(m.match('12 ab 34 cd'))
>>> print(m.findall('12 ab 34 cd'))

<re.Match object; span=(0, 1), match='1'>
['1', '2', 'a', 'b', '3', '4', 'c', 'd']


👩 complie 플래그

  • 정규식을 컴파일 할 때 여러 플래그을 적용할 수 있다.
옵션 역할
DOTALL(S) Dot(.)줄바꿈 문자(\n)를 포함하여 모든 문자와 매치될 수 있다.
IGNORECASE(I) 대소문자와 관계없이 매치 가능하다.
MULTILINE(M) 여러 줄에 대해서 매치할 수 있다.
VERBOSE(X) verbose 모드를 사용할 수 있다. 복잡한 정규식을 이해하기 쉽게 주석을 달 수 있도록 한다.


⬛ DOTALL(S)

  • re.DOTALL / re.s
>>> import re
>>> o = re.compile('a.b')
>>> n = re.compile('a.b', re.DOTALL)

>>> print(o.match('a\nb'))
>>> print(n.match('a\nb'))

None
<re.Match object; span=(0, 3), match='a\nb'>
  • 기존 Dot(.)은 ‘\n’과 매치될 수 없다.
  • re.DOTALL 플래그을 적용할 경우, ‘\n’과 매치 가능하다!


⬛ IGNORECASE(I)

  • re.IGNORECASE / re.I
>>> import re
>>> o = re.compile('[a-z]+')
>>> n = re.compile('[a-z]+', re.IGNORECASE)

>>> print(o.match('Python'))
>>> print(o.match('Python'))

None
<re.Match object; span=(0, 6), match='Python'>
  • [a-z] 정규식은 소문자만을 의미하지만 re.IGNORECASE 플래그를 적용함으로써 대소문자 관계없이 매치가 가능하다!


⬛ MULTILINE(M)

  • re.MULTILINE / re.M
  • 메타 문자 ^, $와 관련이 있는 옵션이다.
    • ^ : 문자열의 처음을 의미한다. ^ 뒤에 있는 문자열로 시작한다는 의미이다.
    • $ : 문자열의 을 의미한다. $ 앞에 있는 문자열로 끝난다는 의미이다.
>>> import re
>>> o = re.compile("^Hello\s\w+")
>>> n = re.compile("^Hello\s\w+", re.MULTILINE)

>>> data = """Hello everybody!
>>> Nice to meet you!
>>> Hello my friends!
>>> Hello my family!
>>> """

>>> print(o.findall(data))
>>> print(n.findall(data))

['Hello everybody']
['Hello everybody', 'Hello my', 'Hello my']
  • “^Hello\s\w+” : Hello로 시작하고 그 후 whitespace + 문자가 와야 매치되는 정규식이다.
  • 플래그를 적용하기 이전에는 data 전체를 하나의 문자열로 인식해 첫 줄만이 매치가 된다.
  • re.MULTILINE 플래그를 적용할 경우, 각 줄마다 매치를 확인하게 된다.


👩 r : raw string

  • 패턴을 명시할 때, 문자를 그 자체로 해석될 수 있도록 하는 기능이다.

🙄 이러한 기능이 왜 필요할까? 아래 예를 통해서 살펴보자!

\direction
  • “\direction”의 문자열을 찾고자 정규식을 만든다고 가정해보자.
  • “\d”는 [0-9]로 해석이 되므로 이는 다음과 같은 정규식으로 매치가 된다.
[0-9]irection

🙄 따라서 “\“문자 자체를 나타내기 위해서는 “\\“를 사용해야 한다.

  • 하지만 “\\“를 사용할 경우, 파이썬 문자열 리터럴 규칙에 따라 “\\“는 “\“로 변경이 된다.
  • 결국 “\“를 사용하기 위해서는 “\\“를 사용해야하는 수고가 생긴다.
>>> import re
>>> m = re.compile(r'\\direction')

>>> print(m.search("123\direction"))
  • 따라서 다음과 같이 r를 사용함으로써 “\“를 문자 자체로 해석할 수 있게 됨으로써 “\direction”을 검색할 수 있게 된다.


👩 re.split() : 구분자 여러 개 적용!

🙄 문자열을 ‘ ‘나 ‘,’ 등 특정 구분자을 가지고 분리하고자 할 때 우리는 split()을 사용한다.

여기서 re.split()은 그 기준을 정규식을 통해서 지정할 수 있다. 즉, 여러 개의 구분자를 적용하고자 할 경우, re.split()을 사용한다.

“100+20*30-90”

🙄 위 문자열을 연산자를 구분자로 하여 분리하고자 할 때, 다음과 같은 정규식을 적용할 수 있다.

exp = "100+20*30-90"
lst = re.split('[*+-]', exp)
print(lst) # ['100', '20', '30', '90']

🙄 그렇다면 숫자를 구분자로 하여 분리해보자!

exp = "100+20*30-90"
lst = re.split('[0-9]+', exp)
print(lst) # 	['', '+', '*', '-', '']


🔔🙋‍♀️정규식에서 () 사용!🔔

공식 문서를 확인하면 split()에 대한 설명 중 다음의 설명이 존재한다.

Split string by the occurrences of pattern. If capturing parentheses are used in pattern, then the text of all groups in the pattern are also returned as part of the resulting list.

즉, ()이 패턴에 사용될 경우, 패턴 내 모든 그룹의 텍스트도 결과에 포함된다는 것이다.

🙄위의 예에서 ()를 포함한 결과를 살펴보자!

exp = "100+20*30-90"
lst = re.split('([*+-])', exp)
print(lst) # 	['100', '+', '20', '*', '30', '-', '90']
  • 구분자로 사용되는 *, +, -도 결과 값에 포함되는 것을 알 수 있다.


📃 참고

댓글남기기