본문 바로가기
점프 투 파이썬

[점프 투 파이썬] 8장 - 정규표현식: 정규 표현식 시작하기

by 눈 떠 보니 공대생 2025. 8. 28.

본 글은 '점프 투 파이썬(박응용)'을 바탕으로 공부한 내용을 정리한 글입니다.

정규 표현식(regular expression): 문자열의 패턴을 표현하는 특별한 문법

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

메타 문자: 문자의 본래 의미가 아니라 특별한 의미를 가진 문자

  • 정규 표현식에 사용하면 특별한 의미를 갖게 되는 메타 문자들
# . ^ $ * + ? { } [ ] \\ | ( )

[ ] 문자 - 문자 클래스

  • 문자 클래스: 이 중에서 아무거나 하나라는 의미
    • e.g. [abc]: a, b, c 중 하나
    • a: 매치됨
    • before: 매치됨
    • done: 매치되지 않음
  • 범위 지정하기 - -(하이픈) 사용법
    • [a-z]: 모든 소문자 알파벳
    • [a-zA-Z]: 모든 알파벳(대, 소문자 모두)
    • [0-9]: 모든 숫자(0부터 9까지)
    • [가-힣]: 모든 한글(가부터 힣까지)
  • 제외하기 - ^ 사용법
    • [^0-9]: 숫자가 아닌 모든 문자(알파벳, 특수 문자, 공백 등)
    • [^A-Z]: 대문자가 아닌 모든 문자
  • 자주 사용하는 문자 클래스
    • \d == [0-9]
    • \D == [^0-9]
    • \s == [ \t\n\r\f\v] (whitespace 문자와 매치)
    • \S == [^ \t\n\r\f\v]
    • \w == [a-zA-Z0-9](문자 + 숫자; alphanumeric)
    • \W == [^a-zA-Z0-9](문자 + 숫자가 아닌 문자)

.(dot) 문자 - \n을 제외한 모든 문자

  • .: \n을 제외한 모든 문자와 매치됨
    • e.g. a.b : a, b 사이 어떤 문자 들어가도 매치
    • aab: 매치됨
    • a0b: 매치됨
    • abc: 매치되지 않음
  • 주의: [.]의 .은 메타 문자가 아니라, ‘.’ 문자 그대로
    • e.g. a[.]b: 문자열 a.b와만 매치됨
    • a.b: 매치됨
    • a0b: 매치되지 않음

*문자

  • * 앞에 있는 문자가 0부터 (이론상) 무한대까지 반복될 수 있음
    • e.g. ca*t: a가 0부터 무한대까지 반복될 수 있음
    • ct: 매치됨(a가 0번 반복)
    • cat: 매치됨(a가 1번 반복)
    • caaat: 매치됨(a가 3번 반복)

+문자

  • +: 앞에 있는 문자가 1부터 무한대까지 반복될 수 있음
    • e.g. ca+t: a가 1번 이상 반복될 수 있음
    • ct: 매치되지 않음(a가 0번 반복)
    • cat: 매치됨(a가 1번 반복)
    • caaat: 매치됨(a가 3번 반복)

{ }문자와 ?문자

  • { } 문자: 반복 횟수 지정
    • e.g. ca{2}t : a를 2번 반복
      • cat: 매치되지 않음(a가 1번 반복)
      • caat: 매치됨(a가 2번 반복)
      • caaat: 매치되지 않음(a가 3번 반복)
    • e.g. ca{2,4}t: a를 2~4회 반복
      • cat: 매치되지 않음(a가 1번 반복)
      • caat: 매치됨(a가 2번 반복)
      • caaat: 매치됨(a가 3번 반복)
  • ? == {0,1} (있어도 되고 없어도 됨)
    • e.g. ab?c : b가 있어도 되고 없어도 됨
      • abc: 매치됨(b가 1번 반복)
      • ac: 매치됨(b가 0번 반복)

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

re 모듈: 표준 라이브러리

패턴: 정규식을 컴파일한 결과

import re
p = re.compile('ab*') # 정규 표현식 컴파일

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

컴파일된 패턴 객체가 제공하는 메서드

Method 목적

match() 문자열의 처음부터 정규식과 매치되는지 조사, match 객체/None으로 반환
search() 문자열 전체 검색해 정규식과 매치되는지 조사, match 객체/None으로 반환
findall() 정규식과 매치되는 모든 substring을 리스트로 반환
finditer() 정규식과 매치되는 모든 substring을 iterator로 반환

match

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

import re
p = re.compile('[a-z]+') # 알파벳 소문자가 1번 이상 반복

m = p.match("python")
print(m) # <re.Match object; span=(0, 6), match='python'>

m = p.match("3 python")
# 처음 나오는 3이 정규 표현식에 부합되지 않으므로
print(m) # None 

search

search(): 문자열 전체를 검색해 정규식과 매치되는지 조사

import re
p = re.compile('[a-z]+') # 알파벳 소문자가 1번 이상 반복

m = p.search("python")
print(m) # <re.Match object; span=(0, 6), match='python'>

m = p.search("3 python")
# 3 이후의 python 문자열과 매치됨
print(m) # <re.Match object; span=(2, 8), match='python'>

findall

findall(): 정규식과 매치되는 모든 substring을 list로 반환

import re
p = re.compile('[a-z]+')
result = p.findall('life is too short')
print(result) # ['life', 'is', 'too', 'short']

finditer

finditer(): 정규식과 매치되는 모든 substring을 iterator로 반환

  • iterator 객체가 포함하는 각 요소는 match 객체
import re
p = re.compile('[a-z]+')
result = p.finditer('life is too short')
print(result) # <callable_iterator object at ...>
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'>
"""

match 객체의 메서드

Method 목적

group() 매치된 문자열 리턴
start() 매치된 문자열의 시작 위치 리턴
end() 매치된 문자열의 끝 위치 리턴
span() 매치된 문자열의 (시작, 끝)에 해당하는 튜플 리턴
import re
p = re.compile('[a-z]+')

m = p.match("python")

print(m.group()) # python
print(m.start()) # 0
print(m.end()) # 6
print(m.span()) # (0, 6)

m = p.search("3 python")

print(m.group()) # python
print(m.start()) # 2
print(m.end()) # 8
print(m.span()) # (2, 8)

컴파일 옵션

DOTALL(S)

.: 줄바꿈 문자 \n를 제외한 모든 문자와 매치됨 → \n 포함하고 싶은 경우 re.DOTALL 또는 re.S 옵션 사용해 컴파일

  • 여러 줄로 이루어진 문자열에서 줄바꿈 문자에 상관없이 검색 시 사용
import re
p = re.compile('a.b', re.DOTALL)
# 또는 p = re.compile('a.b', re.S)
m = p.match('a\\nb')
print(m) # <re.Match object; span=(0, 3), match='a\\nb'>

IGNORECASE(I)

re.IGNORECASE 또는 re.I : 대소문자 구별 없이 매치 수행

import re
p = re.compile('[a-z]+', re.I)
m = p.match('python')
print(m) # <re.Match object; span=(0, 6), match='python'>

m = p.match('PYTHON')
print(m) # <re.Match object; span=(0, 6), match='PYTHON'>

m = p.match('Python')
print(m) # <re.Match object; span=(0, 6), match='Python'>

MULTILINE(M)

re.multiline 또는 re.M: 메타 문자 ^(문자열의 처음), $(끝)을 문자열의 각 줄마다 적용

import re
# 백슬래시 인식 위해 \\\\ 사용
p = re.compile("^python\\\\s\\\\w+") 

"""
python이라는 문자열로 시작
그 뒤에 whitespace
그 뒤에 alphanumeric
"""

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

print(p.findall(data)) # ['python one']
# ^ 에 의해 python이라는 문자열 사용한 첫 번째 줄만 매치
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 : 정규식을 주석 또는 줄 단위로 구분. 문자열에 사용된 주석, 화이트스페이스는 컴파일 시 제거됨

e.g. 이메일 주소 검증하는 정규식

import re
"""
re.VERBOSE 옵션을 사용하지 않은 경우
email_pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$')
"""

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

역슬래시 문제

파이썬 문자열 리터럴 규칙: 문자열 내의 \\ == \ (백슬래시 그 자체)

문자열에 따라 \ 이 반복되는 경우 → raw string 표현법 사용

+) raw string은 정규 표현식 뿐만 아니라 모든 문자열에 대해 사용 가능함

import re
p = re.compile(r'\\\\section') # \\를 1개만 써도 \\ 자체로 인식

예제

import re

# 이메일 주소의 사용자 이름과 도메인 각각 추출해 튜플로 반환
email_pattern = re.compile(r'([a-zA-Z0-9.%_+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})')
text = "제 이메일 주소는 user123@example.com 이고, 비상 연락처는 admin@company.co.kr 입니다."

matches = email_pattern.findall(text)
print(matches)

# 문자열 치환하기
phone_pattern = r'010-\\d{4}-\\d{4}'
text = "고객님 전화번호는 010-1234-5678 이며, 다음 전화번호 010-9876-5432는 가족 번호입니다."
masked = re.sub(phone_pattern, '[전화번호 마스킹]', text)
print(masked)