| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Humble
- CNN
- System Call
- paper review
- BFS
- Baekjoon
- Gentoo2
- Operating System
- do it! 알고리즘 코딩테스트: c++편
- RNN
- Multimedia
- Data Science
- ROS2
- Process
- Python
- On-memory file system
- Linux
- SQLD
- 밑바닥부터 시작하는 딥러닝2
- Machine Learning
- computer vision
- deep learning
- CPP
- file system
- cs231n
- DFS
- Optimization
- C++
- Robocup@Home 2026
- Seoul National University
Archives
- Today
- Total
newhaneul
[Advanced Python Programming] Lecture 18. Regular Expression 본문
4. University Study/Advanced Python Programming
[Advanced Python Programming] Lecture 18. Regular Expression
뉴하늘 2026. 6. 10. 17:34728x90
포스팅은 인하대학교 허혜선 교수님의 [202601-EEC3408-001] 고급파이썬프로그래밍을 수강하고 공부한 내용을 정리하기 위한 포스팅입니다.
1. 정규표현식 (Regular Expression)
정규표현식(Regular Expression)은 일정한 규칙(패턴)을 가진 문자열을 표현하는 방법이다. re 모듈을 사용하며 복잡한 문자열에서 특정 패턴을 검색·추출·치환할 때 활용한다.
match vs search
| 함수 | 설명 | 예시 |
| re.match() | 문자열 처음부터 패턴 검사 | re.match('Hello', 'Hello, world!') |
| re.search() | 문자열 어느 위치든 패턴 검사 | re.search('world!$', 'Hello, world!') |
위치 지정자 ^와 $
import re
print(re.search('^Hello', 'Hello, world!')) # Hello로 시작 → 매칭
print(re.search('world!$', 'Hello, world!')) # world!로 끝남 → 매칭
<re.Match object; span=(0, 5), match='Hello'>
<re.Match object; span=(7, 13), match='world!'>
import re
print(re.match('Hello', 'Hello, world!')) # Hello로 시작 → 매칭
print(re.match('^Hello', 'Hello, world!')) # 매칭 ← ^ 는 사실 match에서 의미 없음 (이미 맨 앞이니까)
print(re.match('world!$', 'Hello, world!')) # $ 의미 없음
<re.Match object; span=(0, 5), match='Hello'>
<re.Match object; span=(0, 5), match='Hello'>
None
OR 패턴 (|)
두 문자열 중 하나라도 포함되면 매칭된다. Python의 or 연산자와 같은 개념이다.
import re
print(re.match('hello|world', 'hello')) # hello 또는 world → 매칭
print(re.search('world|hello', 'hello')) # hello 또는 world → 매칭
<re.Match object; span=(0, 5), match='hello'>
<re.Match object; span=(0, 5), match='hello'>
2. 범위 판단
반복 수량자 * + ? {}
| 기호 | 의미 | 예시 |
| * | 앞 문자 0개 이상 | a*b → b, ab, aab 모두 매칭 |
| + | 앞 문자 1개 이상 | a+b → ab, aab 매칭 / b 불가 |
| ? | 앞 문자 0개 또는 1개 | abc?d → abd, abcd 매칭 |
| . | 임의 문자 1개 | ab.d → abxd, ab3d 매칭 |
| {n} | 정확히 n개 | h{3} → hhh |
| {m,n} | m개 이상 n개 이하 | [0-9]{3,4} → 3~4자리 숫자 |
import re
print(re.match('[0-9]*', '1234'))
print(re.match('[0-9]+', '1234'))
print(re.match('[0-9]+', 'abcd'))
<re.Match object; span=(0, 4), match='1234'>
<re.Match object; span=(0, 4), match='1234'>
None
import re
print(re.match('a*b', 'b'))
print(re.match('a+b', 'b'))
print(re.match('a*b', 'aab'))
print(re.match('a+b', 'aab'))
<re.Match object; span=(0, 1), match='b'>
None
<re.Match object; span=(0, 3), match='aab'>
<re.Match object; span=(0, 3), match='aab'>
import re
print(re.match('abc?d', 'abd'))
print(re.match('ab[0-9]?c', 'ab3c'))
print(re.match('ab.d', 'abxd'))
<re.Match object; span=(0, 3), match='abd'>
<re.Match object; span=(0, 4), match='ab3c'>
<re.Match object; span=(0, 4), match='abxd'>
import re
print(re.match('h{3}', 'hhhello'))
print(re.match('(hello){3}', 'hellohellohelloworld'))
<re.Match object; span=(0, 3), match='hhh'>
<re.Match object; span=(0, 15), match='hellohellohello'>
- 문자 개수 판단하기
- 문자{개수}
- (문자열){개수}
import re
print(re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '123-4567-8901'))
print(re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '123-4567-890'))
<re.Match object; span=(0, 13), match='123-4567-8901'>
None
- 특정 범위의 문자가 몇 개 있는지 판단하는 방법
- [0-9]{개수}
import re
print(re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '123-456-8901'))
print(re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '12-67-1890'))
<re.Match object; span=(0, 12), match='123-456-8901'>
None
- 문자 개수 판단하기
- (문자){시작개수, 끝개수}
- (문자열){시작개수, 끝개수}
- [0-9]{시작개수, 끝개수}
import re
print(re.match('[a-zA-Z0-9]+', 'Hello1234'))
print(re.match('[A-Z0-9]+', 'hello'))
<re.Match object; span=(0, 9), match='Hello1234'>
None
- 숫자와 영문 문자를 조합해서 판단하는 방법
- a-z
- A-Z
문자 범위 [ ]
import re
print(re.match('[^A-Z]+', 'Hello'))
print(re.match('[^A-Z]+', 'hello'))
None
<re.Match object; span=(0, 5), match='hello'>
- 대괄호 안에 ^를 붙이면 해당 범위를 제외한다. [^A-Z]+는 대문자를 제외한 모든 문자와 매칭된다.
- [^범위]*
- [^범위]+
import re
print(re.match('^[A-Z]+', 'Hello'))
print(re.match('^[A-Z]+', 'hello'))
<re.Match object; span=(0, 1), match='H'>
None
- 특정 문자 범위로 시작하는지 알 때는 ^를 문자 범위 앞에 붙여준다.
- ^[범위]+
- ^[범위]*
- [범위]+$
- [범위]*$
import re
print(re.search('\*+', '1 ** 2'))
print(re.match('[$()a-zA-Z0-9]+', '$(document)'))
<re.Match object; span=(2, 4), match='**'>
<re.Match object; span=(0, 11), match='$(document)'>
특수 이스케이프 시퀀스
| 표현 | 의미 |
| \d | [0-9]와 같음 — 모든 숫자 |
| \D | [^0-9]와 같음 — 숫자를 제외한 모든 문자 |
| \w | [a-zA-Z0-9_]와 같음 — 영문·숫자·밑줄 |
| \W | [^a-zA-Z0-9_]와 같음 — 그 외 모든 문자 |
| \s | 공백 문자 (스페이스, 탭, 줄바꿈 등) |
| \S | 공백 이외의 문자 |
- 정규표현식 메타문자(* + ? . ^ $ ( ) [ ] -) 자체를 검색하려면 앞에 \를 붙인다. 예: \*, \.
import re
print(re.match('\d+', '1234'))
print(re.match('\D+', 'Hello'))
print(re.match('\w+', 'Hello_1234'))
print(re.match('\W+', '(:)'))
<re.Match object; span=(0, 4), match='1234'>
<re.Match object; span=(0, 5), match='Hello'>
<re.Match object; span=(0, 10), match='Hello_1234'>
<re.Match object; span=(0, 3), match='(:)'>
import re
print(re.match('[a-zA-Z0-9]+', 'Hello 1234'))
print(re.match('[a-zA-Z0-9\s]+', 'Hello 1234'))
<re.Match object; span=(0, 5), match='Hello'>
<re.Match object; span=(0, 10), match='Hello 1234'>
3. 그룹 사용하기
소괄호로 그룹 지정
패턴을 ()로 묶으면 매칭된 문자열을 그룹별로 꺼낼 수 있다.
import re
m = re.match('([0-9]+) ([0-9]+)', '10 295')
print(m.group(1)) # '10'
print(m.group(2)) # '295'
print(m.group()) # '10 295' (전체)
print(m.groups()) # ('10', '295') (튜플)
10
295
10 295
('10', '295')
그룹에 이름 붙이기 (?P<이름>)
그룹이 많아질 때 숫자 대신 이름으로 접근할 수 있어 가독성이 좋아진다.
import re
m = re.match('(?P<func>[a-zA-Z_][a-zA-Z0-9_]+)\((?P<arg>\w+)\)', 'print(1234)')
print(m.group('func'))
print(m.group('arg'))
print
1234
- (?P<이름>정규표현식)
findall (모든 매칭 추출)
import re
print(re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8'))
['1', '2', '4', '7', '8']
Sub (문자열 치환)
import re
print(re.sub('apple|orange', 'fruit', 'apple box orange tree'))
print(re.sub('[0-9]+', 'n', '1 2 Fizz 4 Buzz Fizz 7 8'))
fruit box fruit tree
n n Fizz n Buzz Fizz n n
import re
def multiple10(m):
n = int(m.group())
return str(n * 10)
print(re.sub('[0-9]+', multiple10, '1 2 Fizz 4 Buzz Fizz 7 8'))
10 20 Fizz 40 Buzz Fizz 70 80
찾은 문자열을 결과에 재활용 (\\숫자)
그룹으로 캡처한 내용을 바꿀 문자열 안에서 \\1, \\2로 참조할 수 있다.
import re
print(re.sub('([a-z]+) ([0-9]+)', '\\2 \\1 \\2 \\1', 'hello 1234'))
print(re.sub('({\s*)"(\w+)":\s*"(\w+)"(\s*})', '<\\2>\\3</\\2>', '{"name": "John"}'))
1234 hello 1234 hello
<name>John</name>
실습 문제 1: 한글 정규표현식
import re
print(re.match('[가-힣]+', '가나다라'))
print(re.findall('[가-힣]+', '가나다라'))
print(re.findall('[가-힣]', '가나다라'))
print(re.findall('[가-힣]+', '가A나B다C라D'))
<re.Match object; span=(0, 4), match='가나다라'>
['가나다라']
['가', '나', '다', '라']
['가', '나', '다', '라']
실습문제 2: 휴대폰 번호 정규표현식
import re
text = "제 번호는 010-1234-5678입니다."
pattern = r'\d{3}-\d{4}-\d{4}'
print(re.findall(pattern, text))
['010-1234-5678']
실습문제 3: 휴대폰, 지역번호
import re
text = "서울: 02-123-4567, 부산: 051-234-5678, 휴대폰: 010-1234-5678"
pattern = r'(\d{2,3})-(\d{3,4})-(\d{4})'
matches = re.findall(pattern, text)
print(matches)
for m in matches:
print("-".join(m))
[('02', '123', '4567'), ('051', '234', '5678'), ('010', '1234', '5678')]
02-123-4567
051-234-5678
010-1234-5678
실습문제 4: 하이픈이 없는 경우까지 포함
import re
text = "전화: 01012345678 또는 010-1234-5678"
pattern = r'\d{3}-?\d{4}-?\d{4}'
matches = re.findall(pattern, text)
print(matches)
['01012345678', '010-1234-5678']
실습문제 5: 이메일 정규표현식
import re
text = "이메일: example123@gmail.com, 다른 이메일은 test.user@mail.co.kr"
pattern = r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9.]+'
print(re.findall(pattern, text))
['example123@gmail.com', 'test.user@mail.co.kr']
ㅈㅂ 1: 전화번호 유효성 검사
import re
# 테스트할 번호들
numbers = ['010-1234-5678', '02-123-4567', '010-123-456', '02-12-4567']
mobile_pattern = r'\d{3}-\d{4}-\d{4}' # 휴대폰: 3자리-4자리-4자리
landline_pattern = r'\d{2,3}-\d{3,4}-\d{4}' # 일반전화: 2~3자리-3~4자리-4자리
for n in numbers:
m = re.match(mobile_pattern, n) or re.match(landline_pattern, n)
print(n, '→', '유효' if m else '무효')
010-1234-5678 → 유효
02-123-4567 → 유효
010-123-456 → 무효
02-12-4567 → 무효
ㅈㅂ 2: HTML 태그 제거
import re
html = '<h1>안녕하세요</h1><p>Python <b>정규표현식</b> 연습입니다.</p>'
print(re.findall(r'<[^>]+>', html))
print(re.sub(r'<[^>]+>', '', html))
['<h1>', '</h1>', '<p>', '<b>', '</b>', '</p>']
안녕하세요Python 정규표현식 연습입니다.
ㅈㅂ 3: 괄호 안에서 가져오기
import re
text = 'print(hello) max(1, 2) range(0, 10, 2) input(name)'
print(re.findall(r'\(([^)]+)\)', text)) # '\( 그룹([^)]+) \)'
# findall은 그룹 ()이 없으면 → 매칭 전체 반환
# 그룹 ()이 있으면 → 그룹 안만 반환
['hello', '1, 2', '0, 10, 2', 'name']
ㅈㅂ 4: 숫자만 가져오기
import re
text = '사과 3개, 가격은 1500원이고 할인율은 12.5%입니다. 총 2봉지.'
result = re.findall(r'\d+\.?\d*|\d', text)
print(result)
['3', '1500', '12.5', '2']
ㅈㅂ 5: = 앞 변수명 가져오기
import re
text = "name = 'John'; _age=25; _student='kwon'"
print(re.findall(r"([a-zA-Z_]\w*)\s?=", text)) # key
print(re.findall(r"=\s?'?([a-zA-Z0-9]+)'?", text)) # value
['name', '_age', '_student']
['John', '25', 'kwon']
Q 3. 아래 HTML에서 <a> 태그 안의 링크 텍스트만 추출해라.
# Q 3. 아래 HTML에서 <a> 태그 안의 링크 텍스트만 추출해라.
import re
html = """
<a href="https://google.com">구글</a>
<a href="https://naver.com">네이버</a>
<a href="https://github.com">깃허브</a>
"""
pattern = r'<a\shref=[^>]+>([가-힣]+)</a>'
print(re.findall(pattern, html))
['구글', '네이버', '깃허브']
Q 4. 아래 HTML에서 href 안의 URL만 추출해라.
# Q 4. 아래 HTML에서 href 안의 URL만 추출해라.
import re
html = """
<a href="https://google.com">구글</a>
<a href="https://naver.com">네이버</a>
<a href="https://github.com">깃허브</a>
"""
pattern = r'<a\shref="([^"]+)">.+</a>'
print(re.findall(pattern, html))
['https://google.com', 'https://naver.com', 'https://github.com']
Q 5. 아래 HTML에서 <span class="price"> 안의 숫자만 추출해라.
# Q 5. 아래 HTML에서 <span class="price"> 안의 숫자만 추출해라.
import re
html = """
<span class="price">12,900</span>
<span class="title">상품명</span>
<span class="price">35,000</span>
<span class="price">8,500</span>
"""
pattern = r'<span\sclass="price">([0-9,]+)</span>'
print(re.findall(pattern, html))
['12,900', '35,000', '8,500']
Q 6. 아래 문자열에서 날짜만 추출해라. (형식: YYYY-MM-DD)
# Q 6. 아래 문자열에서 날짜만 추출해라. (형식: YYYY-MM-DD)
import re
text = """
주문일: 2024-01-15
배송일: 2024-02-03
취소일: 24-3-5
문의일: 2024-12-31
"""
pattern = r'[0-9]{4}-[0-9]{2}-[0-9]{2}'
print(re.findall(pattern, text))
['2024-01-15', '2024-02-03', '2024-12-31']
Q 7. 아래 문자열에서 2024년 이후의 날짜만 추출해라. (형식: YYYY년 MM월 DD일)
# Q 7. 아래 문자열에서 2024년 이후의 날짜만 추출해라. (형식: YYYY년 MM월 DD일)
import re
text = """
2023년 11월 05일 주문
2024년 01월 15일 배송
2024년 12월 31일 완료
2025년 03월 22일 예약
"""
pattern = r'(?:202[4-9]|20[3-9][0-9])년\s[0-9]{2}월\s[0-9]{2}일'
print(re.findall(pattern, text))
['2024년 01월 15일', '2024년 12월 31일', '2025년 03월 22일']
Q 9. 아래 문자열에서 key=value 쌍을 딕셔너리로 만들어라.
# Q 9. 아래 문자열에서 key=value 쌍을 딕셔너리로 만들어라.
import re
text = "name=John; age=25; city=Seoul; job=engineer"
# 여기에 패턴 작성
pairs = re.findall(r'([a-zA-Z_][a-zA-Z_0-9]*)=([a-zA-Z0-9]+)', text)
result = {k: v for k, v in pairs}
print(result)
{'name': 'John', 'age': '25', 'city': 'Seoul', 'job': 'engineer'}
Q 10. 아래 로그에서 ERROR 레벨의 메시지만 추출해라.
# Q 10. 아래 로그에서 ERROR 레벨의 메시지만 추출해라.
import re
log = """
[INFO] 2024-01-15 08:00:01 서버 시작
[ERROR] 2024-01-15 08:05:23 DB 연결 실패
[WARNING] 2024-01-15 08:10:11 메모리 부족
[ERROR] 2024-01-15 08:15:44 타임아웃 발생
[INFO] 2024-01-15 08:20:00 재시작 완료
"""
# 여기에 패턴 작성
result = re.findall(r'\[ERROR\]\s\S+\s\S+\s([^\n]+)', log)
print(result)
['DB 연결 실패', '타임아웃 발생']
728x90
'4. University Study > Advanced Python Programming' 카테고리의 다른 글
| [Advanced Python Programming] Lecture 19. Drawing graphs from web data (1) | 2026.06.11 |
|---|---|
| [Advanced Python Programming] Lecture 17. HTML5 기본 요소 (0) | 2026.06.11 |
| [Advanced Python Programming] Lecture 16. Web Scrapping (0) | 2026.05.27 |
| [Advanced Python Programming] Lecture 15. Web Crawling (0) | 2026.05.27 |
| [Advanced Python Programming] Lecture 13. Data Visualization (0) | 2026.05.25 |