반응형

24. 정규표현식 - 문자열 처리의 끝판왕
"이 텍스트에서 이메일만 쏙 뽑아줘", "전화번호 형식이 맞는지 확인해줘" — 이런 요구를 단 한 줄로 해결하는 방법이 있다. 바로 정규표현식(Regular Expression)이다.
정규표현식이란?
정규표현식(줄여서 regex)은 문자열에서 특정 패턴을 찾거나 치환하는 규칙이다. 파이썬에서는 re 모듈을 사용한다.
import re
text = "제 전화번호는 010-1234-5678입니다."
result = re.search(r"\d{3}-\d{4}-\d{4}", text)
print(result.group()) # 010-1234-5678
처음 보면 암호처럼 보이지만, 하나씩 배우면 생각보다 간단하다.

기본 패턴
메타 문자
| 패턴 | 의미 | 예시 |
|---|---|---|
. |
아무 문자 1개 (줄바꿈 제외) | a.c → "abc", "a1c" |
\d |
숫자 1개 ([0-9]) |
\d\d → "42", "99" |
\w |
영문/숫자/밑줄 1개 | \w+ → "hello", "a1" |
\s |
공백 문자 1개 (스페이스, 탭, 줄바꿈) | \s → " ", "\t" |
\D |
숫자가 아닌 것 | \D+ → "abc", "!@" |
\W |
영문/숫자/밑줄이 아닌 것 | \W → "!", " " |
\S |
공백이 아닌 것 | \S+ → "hello" |
import re
text = "오늘은 2025년 2월 15일입니다."
numbers = re.findall(r"\d+", text)
print(numbers) # ['2025', '2', '15']
반복 패턴
| 패턴 | 의미 |
|---|---|
* |
0회 이상 반복 |
+ |
1회 이상 반복 |
? |
0회 또는 1회 |
{n} |
정확히 n회 |
{n,m} |
n회 이상 m회 이하 |
import re
# * : 0회 이상
print(re.findall(r"ab*c", "ac abc abbc")) # ['ac', 'abc', 'abbc']
# + : 1회 이상
print(re.findall(r"ab+c", "ac abc abbc")) # ['abc', 'abbc']
# ? : 0회 또는 1회
print(re.findall(r"colou?r", "color colour")) # ['color', 'colour']
# {n} : 정확히 n회
print(re.findall(r"\d{4}", "2025년 02월")) # ['2025']
# {n,m} : n~m회
print(re.findall(r"\d{2,4}", "1 12 123 1234")) # ['12', '123', '1234']
위치 패턴
| 패턴 | 의미 |
|---|---|
^ |
문자열의 시작 |
$ |
문자열의 끝 |
import re
print(re.search(r"^Hello", "Hello World")) # 매치 됨
print(re.search(r"^Hello", "Say Hello")) # None (시작이 아님)
print(re.search(r"World$", "Hello World")) # 매치 됨
문자 클래스 [ ]
[ ] 안에 있는 문자 중 하나와 매치된다.
import re
# [abc] - a, b, c 중 하나
print(re.findall(r"[aeiou]", "hello")) # ['e', 'o']
# [0-9] - 숫자 (\d와 같음)
print(re.findall(r"[0-9]+", "abc123def456")) # ['123', '456']
# [a-zA-Z] - 모든 영문자
print(re.findall(r"[a-zA-Z]+", "Hello 세계 123")) # ['Hello']
# [^abc] - a, b, c가 아닌 것 (^ = 부정)
print(re.findall(r"[^0-9]+", "abc123def")) # ['abc', 'def']
re 모듈의 주요 함수
re.match() - 문자열 시작부터 매치
import re
result = re.match(r"\d+", "123abc")
print(result.group()) # 123
result = re.match(r"\d+", "abc123")
print(result) # None (시작이 숫자가 아님)
re.search() - 문자열 어디서든 첫 매치
import re
result = re.search(r"\d+", "abc123def456")
print(result.group()) # 123 (첫 번째만)
re.findall() - 매치되는 모든 것
import re
result = re.findall(r"\d+", "abc123def456ghi789")
print(result) # ['123', '456', '789']
re.sub() - 패턴을 찾아서 치환
import re
# 숫자를 모두 #으로 치환
result = re.sub(r"\d", "#", "전화번호: 010-1234-5678")
print(result) # 전화번호: ###-####-####
# 여러 공백을 하나로
text = "너무 많은 공백이 있다"
result = re.sub(r"\s+", " ", text)
print(result) # 너무 많은 공백이 있다
re.split() - 패턴으로 분리
import re
# 여러 구분자로 분리
text = "사과,바나나;포도 딸기"
result = re.split(r"[,;\s]+", text)
print(result) # ['사과', '바나나', '포도', '딸기']
그룹 (Group)
괄호 ()로 패턴의 일부를 그룹으로 묶을 수 있다. 매치된 결과에서 원하는 부분만 뽑을 때 유용하다.
import re
# 날짜에서 년, 월, 일 추출
text = "오늘은 2025-02-15입니다."
result = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
print(result.group()) # 2025-02-15 (전체 매치)
print(result.group(1)) # 2025 (첫 번째 그룹)
print(result.group(2)) # 02 (두 번째 그룹)
print(result.group(3)) # 15 (세 번째 그룹)
이름 있는 그룹
import re
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
result = re.search(pattern, "2025-02-15")
print(result.group("year")) # 2025
print(result.group("month")) # 02
print(result.group("day")) # 15
실전 활용: 이메일 검증
import re
def is_valid_email(email):
"""이메일 형식 검증"""
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return bool(re.match(pattern, email))
# 테스트
emails = [
"user@example.com",
"my.name@company.co.kr",
"invalid@",
"@no-user.com",
"spaces in@email.com"
]
for email in emails:
result = "유효" if is_valid_email(email) else "무효"
print(f"{email:30s} → {result}")
출력:
user@example.com → 유효
my.name@company.co.kr → 유효
invalid@ → 무효
@no-user.com → 무효
spaces in@email.com → 무효
실전 활용: 전화번호 추출 및 정규화
import re
def normalize_phone(text):
"""다양한 형식의 전화번호를 통일"""
# 다양한 형식 매치: 010-1234-5678, 01012345678, 010 1234 5678
pattern = r"(01[016789])[\s.-]?(\d{3,4})[\s.-]?(\d{4})"
match = re.search(pattern, text)
if match:
return f"{match.group(1)}-{match.group(2)}-{match.group(3)}"
return None
# 테스트
texts = [
"전화번호: 010-1234-5678",
"연락처 01098765432 입니다",
"010 5555 6666으로 전화주세요",
"회사 번호: 02-555-1234"
]
for t in texts:
result = normalize_phone(t)
if result:
print(f"추출: {result}")
else:
print(f"휴대폰 번호 없음: {t}")
자주 쓰는 패턴 모음
import re
# 1. 한글만 추출
text = "Hello 안녕하세요 World 세계"
korean = re.findall(r"[가-힣]+", text)
print(korean) # ['안녕하세요', '세계']
# 2. HTML 태그 제거
html = "<h1>제목</h1><p>내용입니다.</p>"
clean = re.sub(r"<[^>]+>", "", html)
print(clean) # 제목내용입니다.
# 3. URL 추출
text = "사이트: https://www.example.com 방문하세요"
urls = re.findall(r"https?://[\w./\-?=&]+", text)
print(urls) # ['https://www.example.com']
# 4. 중복 공백 정리
text = " 너무 많은 공백 "
clean = re.sub(r"\s+", " ", text).strip()
print(clean) # 너무 많은 공백
직접 해보기
문제 1. 문자열에서 모든 숫자를 추출해 합계를 구하는 함수 sum_numbers(text)를 작성해보자.
문제 2. 비밀번호가 다음 조건을 만족하는지 검증하는 함수를 작성해보자: 8자 이상, 영문 대문자 1개 이상, 소문자 1개 이상, 숫자 1개 이상.
문제 3. 텍스트에서 모든 이메일 주소를 찾아 리스트로 반환하는 함수를 작성해보자.
문제 4. "2025-02-15" 형식의 날짜를 "2025년 02월 15일" 형식으로 바꾸는 함수를 작성해보자.
정답 보기
import re
# 문제 1 - 숫자 합계
def sum_numbers(text):
numbers = re.findall(r"\d+", text)
return sum(int(n) for n in numbers)
print(sum_numbers("사과 3개, 바나나 5개, 포도 12개")) # 20
print(sum_numbers("점수: 85 + 92 + 78")) # 255
# 문제 2 - 비밀번호 검증
def is_valid_password(pw):
if len(pw) < 8:
return False
if not re.search(r"[A-Z]", pw): # 대문자
return False
if not re.search(r"[a-z]", pw): # 소문자
return False
if not re.search(r"\d", pw): # 숫자
return False
return True
print(is_valid_password("MyPass123")) # True
print(is_valid_password("short1A")) # False (8자 미만)
print(is_valid_password("alllowercase1")) # False (대문자 없음)
# 문제 3 - 이메일 추출
def find_emails(text):
pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
return re.findall(pattern, text)
text = "연락처: user@example.com, admin@test.co.kr 로 보내세요"
print(find_emails(text))
# ['user@example.com', 'admin@test.co.kr']
# 문제 4 - 날짜 형식 변환
def convert_date(date_str):
return re.sub(
r"(\d{4})-(\d{2})-(\d{2})",
r"\1년 \2월 \3일",
date_str
)
print(convert_date("2025-02-15")) # 2025년 02월 15일
print(convert_date("오늘은 2025-01-01입니다"))
# 오늘은 2025년 01월 01일입니다
오늘의 정리
| 항목 | 내용 |
|---|---|
| re.match() | 문자열 시작에서 패턴 매치 |
| re.search() | 문자열 어디서든 첫 번째 매치 |
| re.findall() | 매치되는 모든 결과를 리스트로 반환 |
| re.sub() | 패턴을 찾아서 치환 |
| 메타 문자 | \d 숫자, \w 영문숫자, \s 공백, . 아무 문자 |
| 반복 | * 0+회, + 1+회, ? 0~1회, {n,m} n~m회 |
| 그룹 | (패턴)으로 부분 추출, group(1)로 접근 |
다음 편 예고: 가상환경과 프로젝트 관리 - 프로처럼 세팅하기
프로젝트마다 다른 라이브러리 버전을 써야 한다면? 가상환경(venv)과 pip로 깔끔하게 관리하는 방법을 알아보자.
태그: 파이썬 Python 파이썬독학 정규표현식 regex re모듈 패턴매칭 문자열처리 파이썬중급 IT교육
반응형
'Python' 카테고리의 다른 글
| 26. 클래스와 객체 (1) - 나만의 자료형 만들기 (0) | 2026.02.22 |
|---|---|
| 25. 가상환경과 프로젝트 관리 - 프로처럼 세팅하기 (1) | 2026.02.22 |
| 23. 파일 읽기/쓰기 - 데이터를 저장하자 (0) | 2026.02.22 |
| 22. 에러와 예외처리 - 프로그램이 죽지 않게 (0) | 2026.02.22 |
| 21. 모듈과 패키지 - 남이 만든 코드 활용하기 (0) | 2026.02.22 |
댓글