파이썬 기초 6-1. 정규 표현식

MANBOKWAK·2024년 1월 23일

파이썬 기초

목록 보기
14/15

정규 표현식

정규표현식이란?

  • 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용함

  • 파이썬 기초는 아니지만 배워두면 좋다~~

1. 정규 표현식이 필요한 이유

ex) 주민등록번호를 포함하고 있는 텍스트가 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해보자
  • 위와 같은 과제가 있을때 정규식을 모르면 다음과 같은 순서로 프로그램을 작성해야한다

ⓐ. 전체 텍스트를 공백 문자로 나눈다(split).
ⓑ. 나뉜 단어가 주민등록번호 형식인지 조사한다.
ⓒ. 단어가 주민등록번호 형식이라면 뒷자리를 *로 변환한다
ⓓ. 나뉜 단어를 다시 조립한다.

정규표현식을 사용안하고 작성한 경우

data = '''
park 800905-1049118
kwak 700905-1059119
'''

result  = []

for line in data.split("\n"):
    word_result = []
    for word in line.split(" "):
        if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
            word = word[:6] + "-" + "*******"
        word_result.append(word)
    result.append(" ".join(word_result))
print("\n".join(result))

출력화면

정규표현식을 사용해 작성한 경우

import re

data = '''
park 800905-1049118
kwak 700905-1059119
'''

pat = re.compile("(\d{6})[-]\d{7}")
print(pat.sub("\g<1>-*******", data))

출력화면

  • (\d{6}) : 여섯 자리 숫자를 찾음, 괄호를 통해 그룹화했으며 이를 통해 나중에 참조할 수 있음

  • (\d{7}) : 하이픈(-)다음에 오는 7자리 숫자를 찾음, 괄호를 통해 그룹화 했으며 이를 통해 나중에 참조할 수 있음

  • pat = re.compile("(\d{6})[-]\d{7}"): data 문자열 내의 모든 패턴 일치 항목을 새로운 문자열로 대체, "\g<1>-*******"은 첫 번째 그룹 (\d{6})을 참조한 다음 하이픈 과 별표 7개로 대체함


2. 정규 표현식 기초

  • 정규 표현식에서 사용하는 메타 문자는 .^$+?{}\|() 등이 있음

  • 메타문자란 원래 그 문자가 가진 뜻이아닌 특별한 용도로 사용하는 문자를 말함

  • 차근차근 정규 표현식 메타 문자의 의미를 알아보자

① 문자 클래스 []

  • [] 사이 문자들과 매치 라는 의미를 가짐
    ex 1) [abc]라면 -> a, b, c 중 한개의 문자와 매치라는 의미
    ex 2)
    정규식 [ABC]

    문자열매치 여부설명
    aYes"a"에는 정규식과 일치하는 문자 "a"가 있으므로 매치
    beforeYes"before"에는 정규식과 일치하는 문자 "b"가 있으므로 매치
    dudeno"dude"에는 정규식과 일치하는 문자인 a,b,c중 어느 하나도 없으므로 매치 x
  • [] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위를 의미
    ex) [a-c] 는 a,b,c [0-5]는 0,1,2,3,4,5를 의미
  • []를 사용할 때 ^ 메타 문자를 []안에서 사용하면 반대라는 의미를 갖게되므로 사용할대 주의해야함
    ex) [^0-9] 숫자가 아닌 모든 문자가 매치됨

자주 사용하는 정규 표현식

정규 표현식설명
\d숫자와 매치, [0-9]와 동일한 표현식
\D숫자가 아닌것과 매치 [^0-9]와 동일한 표현식
\swhitespace문자와 매치 [ \t\n\r\f\v]와 동일한 표현식, []에서 맨앞 빈칸은 공백 문자를 의미
\Swhitespace가 아닌것과 매치 [^ \t\n\r\f\v]과 동일한 표현식
\w문자 + 숫자와 매치, [a-zA-Z0-9_] 와 동일한 표현식
\W문자 + 숫자 아닌것과 매치 [^a-zA-Z0-9_] 와 동일한 표현식

② Dot (.)

  • Dot(.)은 메타 문자의 줄바꿈 문자인 \n을 제외한 모둔 문자와 매치됨을 의미
    ex1) a.b는 a와 b 사이에 줄바꿈 문자를 제외한 어떤 문자가 들어가도 매치됨

ex 2)
정규식 [a.b]

문자열매치 여부설명
aabYes"aab"는 가운데 문자 "a"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치
a0bYes"a0b"는 가운데 문자 "0"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치
abcNo"abc"는 "a" 문자와 "b" 문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치 x
  • a[.]b에서 [.]은 문자.만을 의미함 a.b와 헷갈리지 않도록 주의!!

③ 반복(*) *

  • *은 반복을 의미

  • * 바로 앞에 있는 문자가 0부터 무한대로 반복되면 매치

ex)
정규식 ca*t

문자열매치 여부설명
ctYes"a"가 0번 반복되어 매치
catYes"a"가 0번 이상 반복되어 매치
caaatYes"a" 0번 이상 반복되어 매치

④. 반복(+)

  • +*가 마찬가지로 반복을 나타내는 정규표현식

  • +는 최소 1번 이상 반복될때마다 사용됨

  • +는 반복이 1부터 시작, *는 0부터 시작
    ex)
    정규식 ca+t

    문자열매치 여부설명
    ctNo"a"가 0번 반복 되어 매치x
    catYes"a"가 1번 이상 반복되어 매치
    caaatYes"a"가 1번이상 반복되어 매치

    ⑤. 반복({m,n}, ?)

  • 반복 횟수를 제한하고 싶을때 사용할 수 있는 메타문자

  • {}메타문자를 사용하면 반복회수를 고정할 수 있음

  • {m,n}반복횟수가 m부터 n까지 매치됨

사용형식 1. {m}
ex) ca{2}t -> a가 2번 반복되면 매치
ca{2}t

문자열매치 여부설명
catNo"a"가 1번 반복되어 매치x
caatYes"a"가 2번 반복되어 매치

사용형식 2.{m,n}
ex) ca{2,5}t -> a가 2~5번 반복되면 매치

ca{2,5}t

문자열매치 여부설명
catNo"a"가 1번만 반복되어 매치x
caatYes"a"가 2번 반복되어 매치
caaaaatYes"a"가 5번 반복되어 매치

사용형식 3. ?
? -> {0,1}을 의미
ab?c

문자열매치 여부설명
abcYes"b"가 1번 사용되어 매치
acYes"b"가 0번 사용되어 매치

3. 파이썬 re 모듈

  • 파이썬은 정규 표현식을 지원하기 위해서 re(regular expression)모듈을 제공

  • re모듈은 파이선을 설치할 때 자동으로 설치되는 기본 라이브리러

  • 사용할때는 re.compile(정규표현식)형식으로 사용

import re

p = re.compile('ab*')
  • re.compile의 결과로 돌려주는 객체를 사용하여 이후의 작업을 수행할 수 있음

4. 정규식을 사용한 문자열 검색

  • re.compile된 패턴 객체를 사용하여 이후의 작업을 수행할 수 있음

  • 문자열 검색을 위해 re모듈은 아래와 같은 메서드를 제공함

    메서드목적
    match()문자열의 처음부터 정규식과 매치되는지 조사한다.
    search()문자열 전체를 검색하여 정규식과 매치되는지 조사한다.
    findall()정규식과 매치되는 모든 문자열(substring)을 리스트로 반환한다.
    findinter()정규식과 매치되는 모든 문자열(substring)을 반복가능한 객체로 반환한다.
  • match, serach는 정규식과 매치될 때는 match객체를 반환해줌

  • 매치되지 않는 경우에는 None을 반환

import re

pat = re.compile('[a-z]+')

#match
m = pat.match("python")
print(m)

m = pat.match("3 python")
print(m)

#search
m = pat.search("3 python")
print(m)

#findall
m = pat.findall("life is too short")
print(m)

#finditer
m = pat.finditer("life is too short")
print(m)
for r in m: print(r)

출력화면!

  • pat.match("python") 에서 python이란 문자열은 [a-z]+ 정규식에 부합되므로 match개체를 반환해주는 것을 확인할 수 있음

  • pat.match("3 python")에서는 매치되지 않으므로 None를 출력하는 것을 확인할 수 있음

  • m = pat.search("3 python") 입력된 문자열에 python이 존재하므로 그 에대한 정보를 담고있는 객체를 반환하고 그 정보를 출력하는것을 확인할 수 있다.

  • pat.findall("life is too short")는 주어진 문자열을 [a-z]+와 매치해서 리스트로 반환해주는것을 확인할 수 있음

  • pat.finditer("life is too short")findall과 결과는 동일하지만 반복가능한 객체를 반환해주는거을 확인할 수 있음 반환된 객체는 matchsearch의 반환 객체와 동일한 객체 (match객체)


5. match객체의 메서드

  • match 객체의 메서드를 사용하면 어떤 문자열이 매치되었는지, 매치된 문자열의 인덱스의 시작과 끝을 확인할 수 있음

    메서드목적
    group()매치된 문자열을 반환
    start()매치된 문자열의 시작 인덱스를 반환
    end()매치된 문자열의 끝 인덱스를 반환
    span()매치된 문자열의 (시작, 끝)에 해당하는 튜플을 반환
import re

pat = re.compile("[a-z]+")
m = pat.match("python")

print(m.group())
print(m.start())
print(m.end())
print(m.span())

출력화면

  • search, finditer메서드도 똑같이 match 객체를 반환하므로 동일하게 사용할 수 있음

6. 컴파일 옵션

  • 정규식을 컴파일 할때 아래와 같은 옵션을 사용할 수 있음

    옵션 이름약어설명
    DOTALLSdot문자(.)가 줄바꿈 문자를 포함하여 모든 문자와 매치
    IGNORECASEI대문자, 소문자 관계없이 매치
    MULTILINEM여러 줄과 매치
    VERBOSEXverbose모드를 사용(정규식의 가독성을 높이고 주석등을 사용할 수 있음)
  • 옵션을 사용할 때는 re.DOTALL or re.S 처럼 전체어 또는 약어를 사용해도 상관없음

①. DOTALL, S

  • . 메타 문자는 줄바꿈 문자를 제외한 모든 문자와 매치되는 규칙이 있음
  • 만약 \n도 포함하여 매치하고 싶다면 re.DOTALL, re.S를 사용해 정규식을 컴파일 하면됨
import re

p = re.compile('a.b')
m = p.match('a\nb')
print(m)

p = re.compile('a.b', re.DOTALL)
m = p.match('a\nb')
print(m)

출력화면

  • 컴파일 옵션을 주지 않을때는 매치가 되지 않기 때문에 None을 반환한다 하지만 re.DOTALL 컴파일옵션을 주면 match객체가 반환되는것을 확인할 수 있음

  • re.DOTALL옵션은 여러 줄로 이루어진 문자열에서 \n에 상관없이 검색할 때 많이 사용함

②. IGNORECASE, I

  • 대문자, 소문자 구별없이 매치를 수행할 때 사용하는 옵션
import re

p = re.compile('[a-z]',re.I)
print(p.match('python'))
print(p.match('Python'))
print(p.match('PYTHON'))

출력화면

  • [a-z] 정규식은 소문자만 의미하지만, re.I 옵션으로 대소문자 구별없이 매치되는것을 확인할 수 있음

③.MULTILINE, M

  • ^,$ 와 연관된 옵션 -> ^는 문자열의 처음을 의미, $는 문자열의 마지막을 의미
# MULTILINE 사용 x Example
import re

p = re.compile("^python\s\w+")

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

print(p.findall(data))

출력화면

  • 정규식 ^python\s\w+은 python 이라는 문자열로 시작하고 그뒤에 whitespce, 그 뒤에 단어가 와야 한다는 의미

  • 메타 문자 ^에 의해 python이라는 문자열을 사용한 첫 번째 줄만 매치됨

# MULTILINE을 사용한 경우 Example
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))

출력화면

  • ^ 메타 문자를 문자열 전체의 처음이 아니라 각 라인의 처음으로 인식시키고 싶은 경우에 사용함

  • ^ 가 각라인에 첫줄에 적용된것을 확인할 수 있음

  • re.MULTILINE 또는 re.M 옵션은 ^,$ 메타 문자를 문자열의 각 줄마다 적용해줌

④. VERBOSE,V

  • 이해하기 어려운 정규식을 주석 또는 줄단위로 구분해 이해를 쉽게할 수 있도록 도와줌
import re

charref = re.compile(r'&[#](0[0-7])+|&#x[0-9a-fA-F]+;')

charref2 = 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옵션을 사용하면 문자열에 사용된 whitespace는 컴파일시 제거됨

  • 줄다위로 #기호를 사용해 주석을 적을 수 있음


7. 백슬래시 문제

  • 정규 표현식을 파이썬에서 사용할 때 혼란을 주는 요소가 바로 \(백슬래시)

  • \section[ \t\n\r\f\v]ection로 해석되어 매치가 제대로 이루어지지 않음

  • 이런 문제를 해결하기 위해서는 \\ 처럼 백슬래시 2개를 사용하면됨

  • BUT, \\는 파이썬 문자열 리터럴 규칙에 따라 \로 변환되고 \\를 전달하기 위해서는 \\\\를 사용해야됨 -> Raw String으로 해결

p = re.compiler(r'//section') 처럼 앞에 문자열 앞에 r을 붙여서 사용하면 \\을 쉽게 전달할 수 있음

profile
Backend/ DevOps를 지망하는 곽희상입니다.

0개의 댓글