정규표현식의 기본
정규표현식(Regular Expression)은 문자열을 처리하는 강력한 도구다.
파이썬에서는 're' 모듈을 통해 정규표현식 기능을 제공한다.
정규표현식을 사용하면 복잡한 문자열 패턴을 간단하게 표현하고 검색, 추출, 치환 등의 작업을 효율적으로 수행할 수 있다.
정규표현식의 기본 문법
1. 문자 클래스
- [abc] : a, b, c 중 하나와 매치
- [a-z]: a부터 z 사이의 모든 소문자와 매치
- [0-9]: 모든 숫자와 매치
2. 메타 문자
- . : 모든 문자와 매치 (단, 개행 문자 제외)
- ^ : 문자열의 시작
- $ : 문자열의 끝
- : 0회 이상 반복
- : 1회 이상 반복
- ? : 0회 또는 1회 반복
- {m,n} : m회 이상 n회 이하 반복
3. 특수 문자 시퀀스
- \d : 숫자와 매치
- \D : 숫자가 아닌 문자와 매치
- \s : 공백 문자와 매치
- \S : 공백이 아닌 문자와 매치
- \w : 문자+숫자와 매치
- \W : 문자+숫자가 아닌 문자와 매치
re 모듈의 주요 함수
파이썬의 re 모듈은 정규표현식을 사용하기 위한 다양한 함수를 제공한다. 주요 함수들은 다음과 같다.
re.match(pattern, string)
문자열의 시작부터 패턴과 일치하는지 검사한다.
re.search(pattern, string)
문자열 전체에서 패턴과 일치하는 부분을 찾는다.
re.findall(pattern, string)
패턴과 일치하는 모든 부분을 리스트로 반환한다.
re.finditer(pattern, string)
패턴과 일치하는 모든 부분을 반복 가능한 객체로 반환한다.
re.sub(pattern, repl, string)
패턴과 일치하는 부분을 다른 문자열로 치환한다.
re.split(pattern, string)
패턴을 기준으로 문자열을 분할한다.
이러한 함수들을 활용하여 다양한 문자열 처리 작업을 수행할 수 있다.
정규표현식 패턴 컴파일
자주 사용하는 정규표현식 패턴은 re.compile() 함수를 사용하여 미리 컴파일해두면 성능을 향상시킬 수 있다.
컴파일된 패턴 객체는 match(), search(), findall() 등의 메서드를 직접 호출할 수 있다.
import re
pattern = re.compile(r'\d+')
result = pattern.findall('abc123def456')
print(result) # ['123', '456']
그룹화와 캡처
정규표현식에서 괄호 ()를 사용하면 패턴의 일부를 그룹화할 수 있다. 그룹화된 부분은 개별적으로 추출하거나 참조할 수 있다.
import re
pattern = re.compile(r'(\w+),(\d+)')
match = pattern.search('name,30')
if match:
print(match.group(1)) # name
print(match.group(2)) # 30
그룹에 이름을 지정할 수도 있다.
pattern = re.compile(r'(?P<name>\w+),(?P<age>\d+)')
match = pattern.search('John,25')
if match:
print(match.group('name')) # John
print(match.group('age')) # 25
정규표현식의 실제 활용 사례
1. 이메일 주소 유효성 검사
이메일 주소의 기본적인 형식을 검사하는 정규표현식 패턴을 만들어보자.
import re
def is_valid_email(email):
pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
return bool(pattern.match(email))
print(is_valid_email('user@example.com')) # True
print(is_valid_email('invalid-email')) # False
2. 전화번호 형식 변환
다양한 형식의 전화번호를 일관된 형식으로 변환하는 예제다.
import re
def format_phone_number(phone):
pattern = re.compile(r'(\d{3})[-.\s]?(\d{4})[-.\s]?(\d{4})')
return pattern.sub(r'\1-\2-\3', phone)
print(format_phone_number('0101234 5678')) # 010-1234-5678
print(format_phone_number('010.9876.5432')) # 010-9876-5432
3. HTML 태그 제거
HTML 문서에서 모든 태그를 제거하고 텍스트만 추출하는 예제다.
import re
def remove_html_tags(html):
pattern = re.compile(r'<[^>]+>')
return pattern.sub('', html)
html = '<p>This is <b>bold</b> text.</p>'
print(remove_html_tags(html)) # This is bold text.
4. 로그 파일 분석
로그 파일에서 특정 패턴의 로그 항목을 추출하는 예제다.
import re
def extract_error_logs(log_file):
pattern = re.compile(r'\[ERROR\] (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (.+)')
error_logs = []
with open(log_file, 'r') as file:
for line in file:
match = pattern.search(line)
if match:
timestamp, message = match.groups()
error_logs.append((timestamp, message))
return error_logs
# 사용 예:
# errors = extract_error_logs('application.log')
# for timestamp, message in errors:
# print(f"{timestamp}: {message}")
5. URL 파싱
URL에서 각 구성 요소를 추출하는 예제다.
import re
def parse_url(url):
pattern = re.compile(r'^(https?://)?([\w.-]+)(/.*)?$')
match = pattern.match(url)
if match:
protocol, domain, path = match.groups()
return {
'protocol': protocol or 'http://',
'domain': domain,
'path': path or '/'
}
return None
print(parse_url('https://www.example.com/path/to/page'))
# {'protocol': 'https://', 'domain': 'www.example.com', 'path': '/path/to/page'}
6. 정규표현식 최적화 팁
1. 과도한 백트래킹 피하기
정규표현식에서 .*
와 같은 패턴을 사용할 때 주의해야 한다. 이는 모든 가능한 경우를 검사하려 하기 때문에 성능 저하를 일으킬 수 있다. 가능한 한 구체적인 패턴을 사용하는 것이 좋다.
2. 비탐욕적(non-greedy) 수량자 사용
*?
, +?
, ??
와 같은 비탐욕적 수량자를 사용하면 필요 이상으로 많은 문자를 매치하는 것을 방지할 수 있다.
3. 앵커 활용
^
와 $
를 사용하여 문자열의 시작과 끝을 명시하면 불필요한 검색을 줄일 수 있다.
4. 적절한 문자 클래스 사용
\d
나 \w
와 같은 특수 문자 시퀀스를 활용하면 코드를 더 간결하고 효율적으로 만들 수 있다.
5. 캡처 그룹 최소화
필요한 경우에만 캡처 그룹을 사용하고, 단순히 그룹화만 필요한 경우 비캡처 그룹 (?:...)
을 사용한다.
7. 정규표현식의 한계와 주의사항
정규표현식은 강력한 도구지만, 모든 문제를 해결할 수 있는 만능 도구는 아니다. 다음과 같은 한계와 주의사항을 알고 있어야 한다.
1. 복잡성
복잡한 패턴을 표현할 때 정규표현식 자체가 매우 복잡해질 수 있다. 이는 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만들 수 있다.
2. 성능 이슈
잘못 작성된 정규표현식은 심각한 성능 저하를 일으킬 수 있다. 특히 큰 데이터셋에 대해 복잡한 패턴을 적용할 때 주의해야 한다.
3. 오버매칭
의도하지 않은 문자열까지 매치될 수 있으므로, 패턴을 신중하게 설계해야 한다.
4. 유지보수의 어려움
복잡한 정규표현식은 나중에 수정하거나 디버깅하기 어려울 수 있다.
5. 검증의 한계
이메일 주소나 URL과 같은 복잡한 형식을 완벽하게 검증하는 것은 정규표현식만으로는 불가능할 수 있다.
따라서 정규표현식을 사용할 때는 그 장단점을 충분히 고려하고, 필요한 경우 다른 방법과 병행하여 사용하는 것이 좋다.
'프로그래밍 > 파이썬' 카테고리의 다른 글
파이썬 - 람다 함수와 함수형 프로그래밍 기초 (0) | 2024.12.01 |
---|---|
파이썬 - 이터레이터(Iterator)와 이터러블(Iterable) (0) | 2024.11.30 |
파이썬 문자열 포매팅: f-string, str.format(), % 연산자 비교 (0) | 2024.11.30 |
파이썬 - 불변(Immutable)과 가변(Mutable) 객체 (0) | 2024.11.29 |
파이썬 - Set (0) | 2024.11.29 |