본문 바로가기
C.W.K.
Stream
Lesson 04 of 06 · published

re — 평평한 문자열 패턴을 찾고 바꾸기

~22 min · re, regex, match, search, compile

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

네 함수부터 익혀

search는 어디서든 첫 일치, match는 시작에서만, findall은 모든 일치, sub는 치환을 맡아. 같은 패턴을 많이 쓰면 compile한 객체로 의도와 비용을 고정해.

패턴 언어와 Python 문자열은 두 층이야

문자 클래스, 반복, 시작·끝, 캡처·이름 캡처·비캡처 그룹, 대안을 조합해. 역슬래시를 두 층에서 해석하지 않도록 정규식 리터럴은 보통 raw 문자열로 써.

중첩 문법에는 실제 파서를 써

전화번호·로그 줄처럼 평평한 형식에는 강하지만 HTML·JSON·괄호 중첩을 정확히 읽는 도구는 아니야.

Code

search·match·findall·sub 비교·python
import re

text = "Pippa is 4 years old. Dad is 50. Pippa loves coding."

# search — 어디든 첫 매치
m = re.search(r"\d+", text)
print(m.group())              # '4'
print(m.span())               # (9, 10) — start, end

# match — 시작에서만 (여기선 None)
print(re.match(r"\d+", text))    # None
print(re.match(r"Pippa", text))  # match 객체

# findall — 모든 매치
print(re.findall(r"\d+", text))     # ['4', '50']
print(re.findall(r"Pippa", text))   # ['Pippa', 'Pippa']

# sub — 교체
print(re.sub(r"\d+", "AGE", text))
# 'Pippa is AGE years old. Dad is AGE. Pippa loves coding.'
캡처 그룹으로 일부 꺼내기·python
import re

log_line = "2026-05-02 15:30:42 [ERROR] Database connection failed"

# 번호 그룹
m = re.match(r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)", log_line)
if m:
    print(m.group(1))         # '2026-05-02'
    print(m.group(2))         # '15:30:42'
    print(m.group(3))         # 'ERROR'
    print(m.group(4))         # 'Database connection failed'
    print(m.groups())         # 모든 그룹 tuple

# 이름 그룹 — 더 읽기 좋음
m = re.match(
    r"(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) \[(?P<level>\w+)\] (?P<msg>.+)",
    log_line
)
if m:
    print(m.group("date"))    # '2026-05-02'
    print(m.group("level"))   # 'ERROR'
    print(m.groupdict())      # 이름 그룹 dict
반복 사용할 패턴 compile하기·python
import re

# 한 번 컴파일
email_re = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+")

emails = ["alice@example.com", "hello world", "bob+tag@x.org"]
for email in emails:
    if email_re.match(email):
        print("유효:", email)
    else:
        print("이메일 아님:", email)

# 컴파일 플래그
case_insensitive = re.compile(r"pippa", re.IGNORECASE)
print(case_insensitive.findall("Pippa, PIPPA, pippa, pippA"))
# ['Pippa', 'PIPPA', 'pippa', 'pippA']
함수로 동적인 치환 만들기·python
import re

text = "Pippa is 4 years old. Dad is 50."

# 각 숫자를 두 배로
def double_age(match):
    age = int(match.group())
    return str(age * 2)

print(re.sub(r"\d+", double_age, text))
# 'Pippa is 8 years old. Dad is 100.'

# Backreference — 교체에서 캡쳐된 그룹 참조
print(re.sub(r"(\w+)@(\w+)", r"\2/\1", "alice@example"))
# 'example/alice'
탐욕적 일치와 이스케이프 함정·python
import re

# 디폴트 greedy
text = '<b>hello</b> <i>world</i>'
print(re.findall(r"<(.+)>", text))      # ['b>hello</b> <i>world</i']
# +? 가 non-greedy
print(re.findall(r"<(.+?)>", text))     # ['b', '/b', 'i', '/i']

# 패턴의 특수 글자는 escape 필요
text = "What is 1+1? It's 2."
print(re.findall(r"\?", text))          # ['?']
print(re.findall(r"\.", text))          # ['.']

# re.escape — regex 에 쓸 리터럴 문자열 escape
lit = "3.14 (special)"
pattern = re.escape(lit)
print(pattern)                          # '3\\.14\\ \\(special\\)'

External links

Exercise

서버 로그 세 줄에서 날짜, 시각, 수준, 메시지, 선택적인 이메일과 IP를 이름 캡처 그룹 하나로 꺼내는 정규식을 compile해. 각 줄의 groupdict를 출력해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.