3 분 소요

정규표현식

문자 클래스 []

[0-9] 또는 [a-zA-Z] 등은 무척 자주 사용하는 정규 표현식이다.

  • \d - 숫자와 매치, [0-9]와 동일한 표현식이다.
  • \D - 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식이다.
  • \s - whitespace 문자와 매치, [ \t\n\r\f\v]와 동일한 표현식이다. 맨 앞의 빈 칸은 공백문자(space)를 의미한다.
  • \S - whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식이다.
  • \w - 문자+숫자(alphanumeric)와 매치, [a-zA-Z0-9_]와 동일한 표현식이다.
  • \W - 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식이다.

대문자로 사용된 것은 소문자의 반대임을 추측할 수 있다.

Dot(.)

정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨을 의미한다.

a.b : "a + 모든문자 + b"
# abc (x)
a[.]b : "a + . + b"

반복 (*)

ca*t : ct, cat, caaaaaaaaat
# a가 0번 이상 반복

반복 (+)

ca+t : cat, caaat # ct(x)
# a가 1번 이상 반복

반복 ({m,n}, ?)

ca{2}t : caat
# a가 2번 반복

ca{2,5}t : caat, caaaaat
# a가 2~5번 반복

ab?c : ac, abc
# b가 있어도되고 없어도 된다. 단, 반복은 아니다. ab{0,1}c 와 같은 의미

파이썬 re 모듈

>>> import re
>>> p = re.compile('[a-z]+') # 정규표현식을 컴파일한다.

정규식을 이용한 문자열 검색

1. match

문자열의 처음부터 정규식과 매치되는지 조사

p = re.complie('[a-z]+')

>>> m = p.match("python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3F9F8>
# 매치되므로 돌려줌

>>> m = p.match("3 python")
>>> print(m)
None
# 매치되지 않음

p = re.compile(정규표현식)
m = p.match( 'string goes here' )
if m:
    print('Match found: ', m.group())
else:
    print('No match')
# 매치되면 돌려주고 아니면 No match 출력

문자열 처음부터 검색하면서 일치하는 부분을 리턴해줌

>>> m = p.search("python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA68>
# 매치되므로 돌려줌

>>> m = p.search("3 python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA30>
# 일치하는 부분만 리턴해줌

3. findall

매치되는 문자열들을 리스트로 리턴해줌

>>> result = p.findall("life is too short")
>>> print(result)
['life', 'is', 'too', 'short']

4. finditer

findall과 동일하지만 반복 가능한 객체(iterator object)로 돌려준다.

>>> result = p.finditer("life is too short")
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result:
    	print(r)
...
<_sre.SRE_Match object at 0x01F3F9F8>
<_sre.SRE_Match object at 0x01F3FAD8>
<_sre.SRE_Match object at 0x01F3FAA0>
<_sre.SRE_Match object at 0x01F3F9F8>

match 객체의 메서드

method 목적
group() 매치된 문자열을 돌려준다.
start() 매치된 문자열의 시작 위치를 돌려준다.
end() 매치된 문자열의 끝 위치를 돌려준다.
span() 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다.
>>> m = p.match("python")
>>> m.group()
'python'
>>> m.start()
0
>>> m.end()
6
>>> m.span()
(0, 6)
>>> m = p.search("3 python")
>>> m.group()
'python'
>>> m.start()
2
>>> m.end()
8
>>> m.span()
(2, 8)

컴파일 옵션

DOTALL, S

re.DOTALL 또는 re.S

  • .은 줄바꿈 문자 \n 를 제외한 모든 것과 매치된다. 컴파일 할 때 re.DOTALL 또는 re.S 옵션을 넣어주면 \n 까지 매치되도록 할 수 있다.
>>> p = re.compile('a.b', re.DOTALL)
# .는 \n을 포함하지 않기 때문에 원래 None이지만, re.DOTALL 또는 re.S를 사용하면 .에 \n도 포함됌

>>> m = p.match('a\nb')

>>> print(m)
<_sre.SRE_Match object at 0x01FCF3D8>

#### IGNORECASE, I

re.IGNORECASE 또는 re.I

  • 대소문자 구별 없이 매치
>>> p = re.compile('[a-z]', re.I)

>>> p.match('python')
<_sre.SRE_Match object at 0x01FCFA30>

>>> p.match('Python')
<_sre.SRE_Match object at 0x01FCFA68>

>>> p.match('PYTHON')
<_sre.SRE_Match object at 0x01FCF9F8>

#### MULTILINE, M

re.MULTILINE 또는 re.M

  • ^ 메타문자를 문자열 전체의 처음이 아니라, 각 라인의 처음으로 인식시킨다. 즉 re.MULTILINE 옵션은 ^, $ 메타 문자를 문자열의 각 줄마다 적용해 주는 것이다.
import re
p = re.compile("^python\s\w+", re.MULTILINE)

data = """
python one
life is too short
python two
you need python
python three
"""

print(p.findall(data))
------------------------------------------
['python one', 'python two', 'python three']

VERBOSE, X

re.VERBOSE 또는 re.X

  • 해당 옵션을 주면 좀 더 가독성 좋게 정규표현식을 작성할 수 있게 된다. 아래의 두 표현식은 동일하게 작동한다.
p = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
p = re.compile(r"""
 &[#]                # Start of a numeric entity reference
 (
     0[0-7]+         # Octal form
   | [0-9]+          # Decimal form
   | x[0-9a-fA-F]+   # Hexadecimal form
 )
 ;                   # Trailing semicolon
""", re.VERBOSE) # re.VERBOSE 옵션 추가

re.VERBOSE 옵션을 추가하면 정규표현식에 컴파일시 자동으로 제거되는 공백코멘트를 추가할 수 있게된다. (단, [] 안에 입력된 공백문자 제외)


Python 정규표현식의 \ 문제

Python에서 정규표현식에 \ 을 사용할 때 아래와 같은 문제가 발생할 수 있다.

text = '\section'
result = re.match('\\section', text)
print(result)
None

\s는 공백문자를 뜻하는 메타문자로 인식되기 때문에 \section 이라는 문자열을 찾으려면 위해 이스케이프 코드 \\를 사용한 \\section 를 정규표현식에 입력해야한다. 그러나 Python 정규식 엔진에서 리터럴 규칙에 의해 \\\로 해석되어 전달된다. 따라서 \\ 를 문자 그대로 넘겨주어야하는데 이를 위해서는 \\\\ 로 입력해야한다.

text = '\section'
result = re.match('\\\\section', text)
print(result)
<_sre.SRE_Match object; span=(0, 8), match='\\section'>

가독성이 심각하게 저하되는데 이를 방지하기 위해 다음과 같이 작성한다.

text = '\section'
result = re.match(r'\\section', text) # raw string을 뜻하는 r을 앞에 붙여준다.
print(result)
<_sre.SRE_Match object; span=(0, 8), match='\\section'>

정규표현식 앞에 r은 항상 붙여주는 것이 권장된다.

댓글남기기