정규 표현식이란?
안녕하세요. 이번에는 파이썬에서 정규 표현식(Regular Expression)을 다루는 방법에 대해 알아보겠습니다. 정규 표현식이란 무엇일까요? 간단히 말해서 문자열 내에서 특정 패턴을 찾거나 치환하는 데 사용되는 표현 형식입니다.
여러분께서는 왜 정규 표현식을 사용해야 하는지 궁금해 하실 것 같습니다. 프로그래밍에서 문자열 처리는 매우 흔한 작업 중 하나인데요. 예를 들어 전화번호에서 '-' 기호를 제거하거나, HTML 태그를 삭제하는 등의 작업을 해야 할 때가 있습니다. 이때 정규 표현식을 사용하면 복잡한 문자열 조작을 간편하게 처리할 수 있습니다.
또한 정규 표현식은 데이터의 유효성 검사에도 많이 사용됩니다. 이메일 주소가 올바른 형식인지, 비밀번호가 특정 규칙을 만족하는지 등을 검사할 때 정규식이 유용하게 활용될 수 있습니다.
이처럼 정규 표현식은 프로그래밍에서 필수적인 도구라고 할 수 있습니다. 쉽지는 않지만 한번 익히고 나면 많은 곳에서 활용할 수 있습니다. 그렇다면 파이썬에서 구체적으로 어떻게 정규 표현식을 사용하는지 알아보도록 하겠습니다.
정규식 모듈 import 하기
파이썬에서는 re 모듈을 통해 정규 표현식을 지원합니다. 따라서 우선 import re로 모듈을 불러와야 합니다.
import re
이후부터는 re모듈 안에 있는 메서드와 상수들을 사용할 수 있습니다.
정규식 패턴 작성하기
정규 표현식에서 패턴이란 찾거나 치환하고자 하는 문자열의 모양을 의미합니다. 패턴은 문자 그대로의 값일 수도 있지만, 정규식 구문을 활용하여 다양한 유형의 패턴을 만들 수 있습니다.
아래는 간단한 정규식 패턴 예시입니다.
- r'python': 문자열 "python"에 매칭
- r'py..n': "py"로 시작하고 "n"으로 끝나며 중간에 임의의 두 문자가 오는 패턴 (pyxxn)
- `r'^\d+``: 숫자로 시작하는 문자열 패턴
r'python'과 같이 정규식 리터럴 앞에 r을 붙이면 이스케이프 시퀀스(\n 등)를 그대로 처리합니다. 정규식에 적합한 문자 그대로의 문자열 패턴이 됩니다.
또한 re 모듈은 정규식에서 사용할 수 있는 약속된 기호들을 제공합니다. 예를 들면 다음과 같습니다.
- .: 모든 문자와 매칭
- \d: 숫자와 매칭
- \w: 문자와 매칭
- \s: 공백 문자(스페이스, 탭 등)와 매칭
- +: 앞의 패턴이 1회 이상 반복되는지 매칭
- *: 앞의 패턴이 0회 이상 반복되는지 매칭
- ?: 앞의 패턴이 0회 또는 1회 존재하는지 매칭
- []: 문자 클래스를 지정 ([abc]는 a, b, c중 하나를 의미)
이러한 기호들을 적절히 조합하여 원하는 패턴을 정규 표현식으로 작성할 수 있습니다. 하지만 정규식이 처음이라면 위 기호들의 의미를 이해하기 어려울 수 있습니다. 그래서 다음과 같이 실제 예시를 통해 하나씩 살펴보면서 이해해 나가는 것이 좋습니다.
- r'[0-9]': 숫자 한 자릿수에 매칭 (0, 1, 2, ... 9)
- r'[0-9]+\.[0-9]+': 소수점 숫자에 매칭 (예: 3.14, 0.456)
- r'\d{3}-\d{4}': 3자리 - 4자리 형태의 숫자에 매칭 (예: 010-1234)
- r'[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+' : 이메일 주소 형식에 매칭 (예: hello@example.com)
- r'</?\w+> : HTML 태그에 매칭
패턴 작성이 조금은 어려울 수 있지만, 반복해서 연습해보면 능숙해질 수 있습니다. 인터넷에도 많은 연습 자료들이 있으니 참고하시기 바랍니다.
정규식 메서드 활용
정규식 패턴을 작성했다면 re 모듈의 메서드를 사용하여 패턴과 매칭되는지 검사하거나 치환할 수 있습니다. 자주 사용되는 메서드는 다음과 같습니다.
1) re.match(pattern, string)
문자열의 시작부분이 정규식 패턴과 매치되는지 확인합니다. 매치되면 match 객체를 반환하고, 매치되지 않으면 None을 반환합니다.
import re
text = "Hello, Python!"
match = re.match(r"Hello", text)
if match:
print("Match!") # Match!
else:
print("Not Match")
2) re.search(pattern, string)
문자열의 전체를 검색하여 정규식 패턴과 매치되는 부분이 있는지 확인합니다. 마찬가지로 매치되면 match 객체를, 매치되지 않으면 None을 반환합니다.
text = "Hello, Python Python!"
match = re.search(r"Python", text)
if match:
print("Match!") # Match!
print(match.group()) # Python
else:
print("Not Match")
3) re.findall(pattern, string)
정규식 패턴과 매치되는 모든 문자열을 리스트로 리턴합니다.
text = "Hello, Python Python Python!"
matches = re.findall(r"Python", text)
print(matches) # ['Python', 'Python', 'Python']
4) re.sub(pattern, repl, string)
정규식 패턴과 매치되는 부분을 repl로 치환합니다.
text = "Hello, Python Python!"
new_text = re.sub(r"Python", "Java", text)
print(new_text) # Hello, Java Java!
이 외에도 re.split(), re.compile() 등의 메서드가 있습니다. match 객체를 통해 패턴 매치 결과를 그룹으로 나누거나, 수정할 수도 있습니다.
옵션 플래그 사용
정규식에는 부가적인 옵션 플래그도 존재합니다. 이 플래그들을 사용하면 패턴 매칭 조건을 세부적으로 조절할 수 있습니다.
- re.I (또는 re.IGNORECASE): 대소문자 구분 없이 매칭
- re.M (또는 re.MULTILINE): 여러 줄과 매칭, ^ 매칭 문자열의 처음이나 각 줄의 첫 부분에 매칭됨
- re.S (또는 re.DOTALL): .이 줄바꿈 문자도 포함하여 모두 매칭
- re.X (또는 re.VERBOSE): 줄 나누기, 주석 등을 활성화하여 정규식 작성 시 가독성 향상
옵션 플래그는 re 모듈의 메서드 호출 시 flags 인자에 조합하여 전달할 수 있습니다.
text = "Hello, PyTHON Python"
match = re.match(r"python", text, re.I) # re.I: 대소문자 무시
if match:
print("Match!") # Match!
else:
print("Not Match")
이처럼 정규식은 다양한 기능들을 제공하기 때문에 꽤 복잡해 보일 수 있습니다. 하지만 많이 사용해보면서 익숙해진다면 강력한 문자열 처리 도구로 활용할 수 있을 것입니다.
실전 예제 및 팁
정규 표현식 사용에 익숙해지려면 여러 예제를 통해 연습해보는 것이 좋습니다. 다음과 같은 실전 예제들을 한번 따라해보시기 바랍니다.
예시 1) HTML 태그 제거
import re
text = """
<h1>Title</h1>
<p>Content</p>
<div>This is div</div>
"""
clean_text = re.sub(r"</?[a-z]+>", "", text)
print(clean_text)
# 출력 결과:
# Title
# Content
# This is div
</?[a-z]+>는 시작 태그(<태그>)와 종료 태그(</태그>) 모두에 매칭되는 패턴입니다. re.sub()로 빈 문자열로 치환하면 태그만 제거된 문자열을 얻을 수 있습니다.
예시 2) 이메일 주소 추출
import re
text = "My email is john@example.com and jane@example.co.kr"
emails = re.findall(r"[a-zA-Z]+@[a-z\.]+", text)
print(emails) # ['john@example.com', 'jane@example.co']
[a-zA-Z]+@[a-z\.]+는 이메일 주소 형식에 매칭되는 패턴입니다. re.findall()로 이 패턴과 매칭되는 문자열들을 추출할 수 있습니다.
예시 3) 전화번호 정제
import re
text = "My number is 010-1234-5678 and 123-456-7890"
numbers = re.sub(r"\D", "", text)
print(numbers) # 01012345678123456789
\D는 숫자가 아닌 문자를 의미합니다. re.sub()로 문자열에서 \D에 매칭되는 부분(하이픈, 공백 등)을 제거하면 숫자만 남게 됩니다.
팁
- 패턴은 간단하게 시작하세요. 패턴을 너무 복잡하게 만들면 유지보수가 힘들어집니다. 단계적으로 복잡도를 높여가는 것이 좋습니다.
- 정규식 테스트 사이트를 활용하세요. 예를 들면 https://regex101.com/에서 패턴을 테스트해볼 수 있습니다.
- 주석을 활용해 가독성을 높이세요. 특히 복잡한 패턴일수록 re.X 옵션과 함께 사용하면 코드 이해가 쉬워집니다.
- 최신 버전의 파이썬을 사용하세요. 최신 버전의 파이썬은 더 나은 정규식 엔진을 포함하고 있습니다.
- 반복문과 함께 사용하세요. 정규식과 반복문을 함께 사용하면 문자열 전체에 대한 복잡한 처리가 가능해집니다.
- 성능을 고려하세요. 정규식은 편리하지만 너무 복잡하면 성능 저하가 올 수 있습니다. 최적화가 필요한 경우 다른 대안을 고려해보세요.
정규식 컴파일과 성능 고려 사항
지금까지 정규 표현식의 기본 개념과 사용법을 알아보았습니다. 하지만 정규식을 실제 프로젝트에서 효율적으로 활용하기 위해서는 몇 가지 추가적인 고려 사항이 있습니다.
1. 정규식 컴파일
정규식은 파이썬 내부적으로 매번 다시 파싱되어 패턴 객체로 변환됩니다. 이 과정에서 많은 부하가 발생할 수 있습니다. re.compile()을 사용하면 패턴을 한 번만 파싱한 뒤 해당 객체를 재사용할 수 있습니다.
import re
pattern = re.compile(r'[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,4}')
emails = pattern.findall('john@example.com, jane@example.co.kr')
print(emails) # ['john@example.com', 'jane@example.co.kr']
위 예시에서는 이메일 패턴을 re.compile()로 컴파일하여 pattern 객체에 할당합니다. 이후 pattern.findall()과 같이 호출하면 컴파일 과정을 건너뛸 수 있습니다.
2. 정규식 최소화하기
정규식 구문이 너무 복잡해지면 성능 저하의 원인이 됩니다. 불필요한 부분을 제거하거나 간단한 패턴으로 분리하는 등의 최적화가 필요합니다.
3. 반복 문자열 처리에 정규식 사용하지 않기
반복되는 단순 문자열은 문자열 메서드를 활용하는 것이 더 빠릅니다. 정규식은 복잡한 패턴 처리에만 사용하는 것이 좋습니다.
4. 백트래킹 문제 고려하기
정규식 엔진은 텍스트를 처음부터 마지막까지 확인합니다. 이때 첫 번째로 시도한 매칭이 실패하면 그 부분으로 돌아가 다음 매칭을 시도하는 백트래킹(Backtracking) 과정이 수반됩니다. 따라서 반복 메타 문자(*,+)를 너무 많이 사용하면 백트래킹 횟수가 기하급수적으로 늘어나 성능 저하가 발생할 수 있습니다.
이럴 때는 가능한 한 매칭 범위를 좁혀 백트래킹이 발생하지 않도록 패턴을 작성하는 것이 중요합니다.
5. 과도한 그룹핑 피하기
정규식에서 그룹핑은 매칭된 부분 문자열을 추출하는 데 유용합니다. 하지만 너무 많은 그룹을 지정하면 불필요한 메모리 할당과 연산이 발생할 수 있습니다.
그룹핑이 필요한 상황에서만 괄호를 사용하고, 그렇지 않은 경우에는 비캡처링 그룹(?:)을 사용하면 메모리를 절약할 수 있습니다.
6. 정규식 디버깅과 테스트
복잡한 정규식 패턴일수록 버그가 발생하기 쉽습니다. 따라서 패턴을 만들 때마다 여러 테스트 케이스를 만들어 검증하는 것이 좋습니다. 또한 re.X 옵션을 사용하면 정규식을 주석과 함께 작성할 수 있어 가독성이 높아집니다.
import re
pat = re.compile(r"""
[\w\.-]+ # 계정 이름 (문자, 숫자, 언더스코어, 점, 대시)
@ # @ 기호
( # 도메인 이름 그룹
\w+ # 하위 도메인, 적어도 하나의 단어 문자
(\.\w+)* # 점 뒤에 추가 도메인이 올 수 있음(반복 가능)
)
""", re.X)
email = 'john@example.com'
match = pat.search(email)
if match:
print(match.group()) # john@example.com
re.X 플래그와 함께 사용하면 주석과 여러 줄로 작성된 정규식을 더 읽기 쉽게 만들 수 있습니다.
정규 표현식은 강력한 기능을 제공하지만 성능 문제나 버그 발생 위험도 있습니다. 따라서 상황에 맞게 정규식 패턴을 잘 선택하고, 컴파일과 최적화 기법, 주석과 테스트 등을 통해 효율적이고 안전하게 사용해야 합니다.
정규식의 대체재와 향후 개선 방향
정규 표현식은 꽤 오랜 역사를 가지고 있습니다. 그 탄생 초기부터 많은 장점과 단점이 있었고, 이를 극복하기 위한 노력들이 계속되어 왔습니다.
최근에는 정규식의 대체재로 구조적 패턴 매칭(Structural Pattern Matching) 기법들이 주목받고 있습니다. 이는 정규식보다 직관적이고 에러를 범하기 어려우며, 파싱 실패에 허용적입니다.
대표적인 구조적 패턴 매칭 기법으로는 다음과 같은 것들이 있습니다.
- Glob 패턴: 파일 경로명에 사용되는 패턴으로 *(0회 이상 반복), ?(임의의 한 문자), [](괄호 안 문자 집합) 등의 구문을 지원합니다.
- XML/HTML 파서: 구조적 텍스트 데이터를 파싱할 때 사용하는 전용 파서 모듈입니다.
- 파이썬 데이터 클래스 (dataclass): 객체에 대한 구조적 패턴 매칭을 지원합니다(파이썬 3.10부터).
다음은 구조적 패턴 매칭을 활용한 예시 코드입니다.
from dataclasses import dataclass
@dataclass
class Employee:
id: int
name: str
dept: str
salary: float
employees = [
Employee(101, "John", "Engineering", 80000.0),
Employee(102, "Jane", "Sales", 70000.0),
Employee(103, "Jack", "Engineering", 90000.0)
]
for emp in employees:
match emp:
case Employee(id=101, name="John", dept=dept):
print(f"John is in {dept} department")
case Employee(id, name, dept="Sales", salary=salary):
print(f"{name} in Sales department earns ${salary:.2f}")
case _:
print(f"Unknown employee: {emp}")
이 예시에서는 dataclass와 match/case 구문을 사용하여 구조적으로 데이터를 처리합니다. 정규식보다 쉽고 명확하며, 오류를 범하기 어렵습니다.
물론 위와 같은 대체재들이 정규식을 완전히 대체할 수는 없습니다. 문자열 데이터를 처리할 때는 여전히 정규식이 유리한 면이 있습니다. 그래서 최근에는 정규식에도 새로운 기능들이 추가되고 있습니다.
파이썬 3.11부터는 새로운 정규식 엔진이 도입될 예정이라고 합니다. 이 새 엔진은 기존 엔진보다 성능이 뛰어나고 더 진보된 표현식을 지원할 것이라고 하네요.
이처럼 정규 표현식은 계속해서 발전하고 있습니다. 대체재들과 상호 보완하며 문자열 처리 분야에서 중요한 역할을 해나갈 것으로 기대됩니다. 정규식에 대한 이해도를 높이는 한편, 관련 기법들도 지속해서 익히는 자세가 필요할 것 같습니다.
정리
지금까지 파이썬에서 정규 표현식을 다루는 방법에 대해 자세하게 살펴보았습니다. 정규식은 프로그래밍에서 문자열을 효과적으로 처리하기 위한 필수적인 도구입니다. 그 중요성과 활용법을 간략히 요약하면 다음과 같습니다.
- 정규식은 문자열에서 특정 패턴을 찾거나 치환하는 데 사용됩니다. 데이터 검증, 전처리 등 다양한 곳에서 활용됩니다.
- 파이썬에서는 re 모듈로 정규식을 다룹니다. 여기에는 패턴 매칭, 검색, 치환 등의 메서드가 있습니다.
- 정규식 패턴은 메타 문자와 정규 구문으로 구성됩니다. 조금 어렵지만 예제와 연습으로 익힐 수 있습니다.
- re.compile() 등을 사용해 성능 개선할 수 있지만, 과도한 백트래킹이나 그룹핑은 오히려 저하시킬 수 있습니다.
- 정규식은 디버깅과 테스트가 필수적입니다. re.X 옵션과 관련 도구를 활용하면 좋습니다.
- 정규식의 대체재로 구조적 패턴 매칭 기법들이 등장했지만, 정규식도 지속 발전 중에 있습니다.
파이썬 정규 표현식은 다소 어려운 주제일 수 있습니다. 하지만 꾸준히 연습하고 실전에 적용해본다면 그 진가를 경험할 수 있을 것입니다. 문자열 처리에 많은 도움이 될 것입니다. 부족한 점이 있다면 추가 설명 요청해주시기 바랍니다.
참고 자료
[1] Python Documentation, "Regular Expression"
[2] Luciano Ramalho, "Fluent Python"
[3] Michael C. Feathers, "Structural Pattern Matching"
[4] Brett Slatkin, "Effective Python"
한 고대 문서 이야기
여기 한 고대 문서가 있습니다. 이 문서는 B.C. 1,500년 부터 A.D 100년까지 약 1,600 여 년 동안 기록되었습니다. 이 문서의 저자는 약 40 명입니다. 이 문서의 고대 사본은 25,000 개가 넘으나, 사본간 오
gospel79.tistory.com
유튜브 프리미엄 월 1만원 할인받고 월 4000원에 이용하는 방법
올해 5월부터 월 8000원 정도이던 유튜브 프리미엄 요금이 15000원 정도로 인상됩니다. 각종 OTT 서비스, ChatGPT 같은 서비스들이 늘어나다보니 이런 거 몇 개만 이용하더라도 월 이용요금이 5만원을
stock79.tistory.com
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
'IT > 파이썬 기초 완전 정복' 카테고리의 다른 글
파이썬 리스트 컴프리헨션 활용법 (17) (1) | 2024.04.20 |
---|---|
파이썬 람다 함수 기초 (16) (0) | 2024.04.20 |
파이썬 문자열 처리 기초 (14) (1) | 2024.04.20 |
파이썬 집합 자료형 사용하기 (13) (1) | 2024.04.20 |
파이썬 딕셔너리 자료구조 익히기 (12) (3) | 2024.04.20 |
댓글