[Python] 정규 표현식

shlim55·2025년 11월 9일

Python

목록 보기
20/25

08-1 정규 표현식 살펴보기

정규 표현식(regular expressions)은 문자열의 패턴을 표현하는 특별한 문법이다.

정규 표현식은 왜 필요한가?

주민등록번호를 포함하고 있는 텍스트가 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.

일때 정규식을 모르는 사람일 경우

data = """
park 800905-1049118
kim  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))

park 800905-*******
kim  700905-*******

이렇게 코딩할것이다.

굉장히 복잡하다.
그래서 이런 문자열을 처리할 때 단 두줄로 해결이 가능하다.

import re 

data = """
park 800905-1049118
kim  700905-1059119
"""

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

08-2 정규 표현식 시작하기

정규 표현식의 기초, 메타 문자

정규 표현식에 다음과 같은 메타 문자를 사용하면 특별한 의미를 갖게 된다.
. ^ $ * + ? { } [ ] \ | ( )

문자

기호 의미 예시
. 줄바꿈 문자를 제외한 모든 문자 하나 a.b → acb, a1b

  • 0번 이상 반복 ca*t → ct, cat, caaat
  • 1번 이상 반복 ca+t → cat, caaat (단, ct는 아님)
    ? 0번 또는 1번 반복 (선택적) colou?r → color, colour
    {m, n} m번부터 n번까지 반복 a{2,4} → aa, aaa, aaaa

파이썬에서 정규 표현식을 지원하는 re 모듈

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

Method 목적
match() 문자열의 처음부터 정규식과 매치되는지 조사한다.
search() 문자열 전체를 검색하여 정규식과 매치되는지 조사한다.
findall() 정규식과 매치되는 모든 문자열(substring)을 리스트로 반환한다.
finditer() 정규식과 매치되는 모든 문자열(substring)을 반복 가능한 객체로 반환한다.

match

import re
p = re.compile('[a-z]+')
m = p.match("python")
print(m)

<re.Match object; span=(0, 6), match='python'>

이렇게 매치가 성공하면 매치 객체가 나온다.

즉, match의 결괏값이 있을 때만 그다음 작업을 수행하겠다는 것이다.

패턴이랑 매치가 되지 않으면 뭐가 나올까?

3 python으로 수정시 None이 나온다.

search

import re
p = re.compile('[a-z]+')
m = p.search("python")
print(m)

<re.Match object; span=(0, 6), match='python'>

패턴틀리게 3 python으로 수정시
<re.Match object; span=(2, 8), match='python'>
가뜸

매치가 된걸 볼수 있다

매치 메서드는 처음부터 이걸 검사하게 되는데
서치는 처음부터가 아니더라도 뒤에서부터 매치가 되도
매치객체를 돌려준다.

findall

result = p.findall("life is too short")
print(result)

['life', 'is', 'too', 'short']
매치되는 것을 리스트 형태로 돌려줌

finditer

import re
p = re.compile('[a-z]+')
result = p.finditer("life is too short")
for r in result:
    print(r)

<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>

반복 가능한 객체(iterator object)를 반환한다.

• re.search(): 문자열 전체를 검색해서 첫 번째로 매칭되는 객체를 찾을 때
• re.match(): 문자열의 시작 부분에서만 매칭되는지 확인할 때
• re.findall(): 매칭되는 모든 부분을 리스트로 반환받을 때
• re.sub(): 매칭되는 부분을 다른 문자열로 치환할 때

match 객체의 메서드

method 목적
group 매치된 문자열을 반환한다.
start 매치된 문자열의 시작 위치를 반환한다.
end 매치된 문자열의 끝 위치를 반환한다.
span 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 반환한다.

import re
p = re.compile('[a-z]+')
m = p.match("python")
print(m.group())
print(m.start())
print(m.end())
print(m.span())

python
0
6
(0, 6)

이것을 서치로 수정하면

import re
p = re.compile('[a-z]+')
m = p.search("3 python")
print(m.group())
print(m.start())
print(m.end())
print(m.span())

python
2
8
(2, 8)

서치는 처음부터 매칭을 확인하지 않기 때문에 이렇게

중간에 나와도 매치 객체를 받을 수가 있다.

두번째 인덱스부터 시작되서 2가 나옴
엔드는 8미만이니 8이 찍힘

컴파일 옵션

• DOTALL(S) - .(dot)이 줄바꿈 문자를 포함해 모든 문자와 매치될 수 있게 한다.
• IGNORECASE(I) - 대소문자에 관계없이 매치될 수 있게 한다.
• MULTILINE(M) - 여러 줄과 매치될 수 있게 한다. ^, $ 메타 문자 사용과 관계 있는 옵션이다.
• VERBOSE(X) - verbose 모드를 사용할 수 있게 한다. 정규식을 보기 편하게 만들 수 있고 주석 등을 사용할 수 있게 된다.

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

None이출력된다.

하지만 여기서 re.DOTALL을 추가하면

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

<re.Match object; span=(0, 3), match='a\nb'>

매치 객체를얻는것을 볼수 있다.

IGNORECASE, I

대소문자 구별 없이 매치를 수행할 때 사용하는 옵션이다.

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

이렇게 했을때는 맨처음 python만 일치해서
<re.Match object; span=(0, 6), match='python'>
None
None
매치 객체를 첫번째거만 얻을수 있다.

그렇다면 re.I 옵션을 주면 대소문자 구분 없이 매칭이 된다.

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

<re.Match object; span=(0, 6), match='python'>
<re.Match object; span=(0, 6), match='Python'>
<re.Match object; span=(0, 6), match='PYTHON'>

MULTILINE, M

# multiline.py
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 one'] 첫번째 줄만 출력하게 됨

^ 메타 문자를 문자열 전체의 처음이 아니라 각 라인의 처음으로 인식시키고 싶은 경우
-> re.MULTILINE 또는 re.M 사용

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

['python one', 'python two', 'python three']

VERBOSE, X

어려운 정규식을 주석 또는 줄 단위로 구분

re.VERBOSE 또는 re.X 옵션을 사용

emailpattern = re.compile(r'^[a-zA-Z0-9.%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$')
어려운 정규식에 주석을 추가한다

emailpattern = re.compile(r"""
^ # 문자열의 시작
[a-zA-Z0-9.
%+-]+ # 사용자명: 영문자, 숫자, 특수문자
@ # @ 기호
[a-zA-Z0-9.-]+ # 도메인명: 영문자, 숫자, 점, 하이픈
. # 점(.)
[a-zA-Z]{2,} # 최상위 도메인: 영문자 2자 이상
$ # 문자열의 끝
""", re.VERBOSE)

re.VERBOSE 옵션을 사용하면 문자열에 사용된 주석과 화이트스페이스는 컴파일할 때 제거된다(단, [ ] 안에 사용한 화이트스페이스는 제외).

역슬래시 문제

\ 문자가 문자열 자체라는 것을 알려 주기 위해 역슬래시 2개를 사용해 이스케이프 처리를 해야 한다.
따라서 위 정규식을 컴파일하려면 다음과 같이 작성해야 한다.

>>> p = re.compile('\\section')

결국 정규식 엔진에 \ 문자를 전달하려면 파이썬은 \\처럼 역슬래시를 4개나 사용해야 한다.

>>> p = re.compile(r'\\section')

이와 같이 정규식 문자열 앞에 r 문자를 삽입하면 이 정규식은 raw string 규칙에 의해 역슬래시 2개 대신 1개만 써도 2개를 쓴 것과 동일한 의미를 가지게 된다.

profile
A Normal Programmer

0개의 댓글