정규표현식(regular expression)은 문자열에서 특정 패턴을 찾거나, 추출하거나, 치환할 때 사용하는 패턴 언어입니다.
파이썬에서는 re 모듈로 정규표현식을 사용합니다. 생물정보학에서는 시퀀스 패턴 검색, 파일 파싱, 헤더 정보 추출 등에 자주 활용됩니다.
기본 패턴 문자
| 패턴 | 의미 | 예시 |
| . | 임의의 문자 한 개 (줄바꿈 제외) | A.C → ABC, ATC |
| ^ | 문자열의 시작 | ^AT → AT로 시작 |
| $ | 문자열의 끝 | CG$ → CG로 끝 |
| * | 앞 문자가 0개 이상 | AT* → A, AT, ATT |
| + | 앞 문자가 1개 이상 | AT+ → AT, ATT |
| ? | 앞 문자가 0개 또는 1개 | AT? → A, AT |
| {n} | 앞 문자가 정확히 n개 | A{3} → AAA |
| {n,m} | 앞 문자가 n개 이상 m개 이하 | A{2,4} → AA, AAA, AAAA |
| [] | 문자 클래스 (안의 문자 중 하나) | [ATCG] → A, T, C, G 중 하나 |
| [^] | 부정 문자 클래스 | [^ATCG] → A,T,C,G 외의 문자 |
| | | 또는 | AT|GC → AT 또는 GC |
| () | 그룹 | (AT)+ → AT, ATAT |
| \d | 숫자 ([0-9]와 동일) | \d+ → 연속된 숫자 |
| \w | 영문자, 숫자, 언더스코어 | \w+ → 단어 |
| \s | 공백 문자 (스페이스, 탭, 개행) | \s+ → 연속된 공백 |
| \D, \W, \S | 위 각각의 반대 |
정규표현식에서 \가 들어가는 패턴을 사용할 때는 raw string(r"")을 사용하는 것이 좋습니다.
import re
# \d가 이스케이프 문자로 해석되지 않도록 raw string 사용
pattern = r"\d+"
re 모듈 주요 함수
re.search()
문자열 전체에서 패턴과 일치하는 첫 번째 위치를 찾습니다.
찾으면 매치 객체를, 찾지 못하면 None을 반환합니다.
import re
seq = "ATCGATCGATCG"
match = re.search(r"CGA", seq)
if match:
print(match.group()) # CGA (매칭된 문자열)
print(match.start()) # 2 (시작 인덱스)
print(match.end()) # 5 (끝 인덱스)
print(match.span()) # (2, 5)
re.match()
문자열의 시작 부분에서만 패턴을 찾습니다.
시작 부분이 아니면 None을 반환합니다.
# FASTA 헤더 확인
header = ">sample_01 Homo sapiens"
if re.match(r">", header):
print("FASTA 헤더")
re.search()는 문자열 전체를 탐색하고, re.match()는 시작 부분만 확인합니다.
시작 부분을 확인하려면 re.match() 또는 re.search(r"^패턴")을 사용합니다.
re.findall()
패턴과 일치하는 모든 결과를 리스트로 반환합니다.
seq = "ATCGATCGATCG"
# 모든 'AT' 위치 찾기
matches = re.findall(r"AT", seq)
print(matches) # ['AT', 'AT', 'AT']
# 숫자 추출
text = "sample_01: 15000000 reads, coverage: 30.5x"
numbers = re.findall(r"\d+\.?\d*", text)
print(numbers) # ['01', '15000000', '30.5']
# 생물정보학 예시: 제한효소 인식 서열 찾기
seq = "AAAGAATTCGGGAATTCTTTT"
ecori_sites = re.findall(r"GAATTC", seq)
print(f"EcoRI 인식 부위 수: {len(ecori_sites)}") # 2
re.finditer()
findall()과 달리 매치 객체를 하나씩 반환하는 이터레이터입니다.
매칭 위치 정보가 필요할 때 사용합니다.
seq = "AAAGAATTCGGGAATTCTTTT"
for m in re.finditer(r"GAATTC", seq):
print(f"위치: {m.start()}-{m.end()}")
# 위치: 3-9
# 위치: 12-18
re.sub()
패턴과 일치하는 부분을 다른 문자열로 치환합니다.
# 기본 치환
seq = "ATCGatcgATCG"
cleaned = re.sub(r"[a-z]", "", seq) # 소문자 제거
print(cleaned) # ATCGATCG
# count 인자로 치환 횟수 제한
text = "sample sample sample"
result = re.sub(r"sample", "input", text, count=2)
print(result) # input input sample
# 그룹을 활용한 치환
header = ">sample_01 | Homo sapiens | chr1"
new_header = re.sub(r">(\S+).*", r">\1", header)
print(new_header) # >sample_01
re.split()
패턴을 구분자로 문자열을 분리합니다.
# 쉼표, 세미콜론, 탭 등 여러 구분자로 분리
line = "BRCA1,TP53;EGFR\tKRAS"
genes = re.split(r"[,;\t]", line)
print(genes) # ['BRCA1', 'TP53', 'EGFR', 'KRAS']
# 연속된 공백으로 분리
text = "sample_01 Homo sapiens 30.5"
fields = re.split(r"\s+", text.strip())
print(fields) # ['sample_01', 'Homo sapiens', '30.5']
그룹과 캡처
소괄호 ()로 패턴의 일부를 그룹으로 묶으면 해당 부분을 별도로 추출할 수 있습니다.
# FASTQ 헤더에서 정보 추출
header = "@SRR1234567.1 length=150"
match = re.search(r"@(\w+)\.(\d+) length=(\d+)", header)
if match:
print(match.group(0)) # @SRR1234567.1 length=150 (전체 매칭)
print(match.group(1)) # SRR1234567 (첫 번째 그룹)
print(match.group(2)) # 1 (두 번째 그룹)
print(match.group(3)) # 150 (세 번째 그룹)
print(match.groups()) # ('SRR1234567', '1', '150')
이름 있는 그룹
그룹에 이름을 붙이면 인덱스 대신 이름으로 접근할 수 있어 가독성이 높아집니다.
header = "@SRR1234567.1 length=150"
match = re.search(r"@(?P<run_id>\w+)\.(?P<read_num>\d+) length=(?P<length>\d+)", header)
if match:
print(match.group("run_id")) # SRR1234567
print(match.group("read_num")) # 1
print(match.group("length")) # 150
print(match.groupdict())
# {'run_id': 'SRR1234567', 'read_num': '1', 'length': '150'}
컴파일 (re.compile)
같은 패턴을 여러 번 사용한다면 re.compile()로 미리 컴파일해두면 성능이 향상됩니다.
pattern = re.compile(r"GAATTC") # 한 번만 컴파일
sequences = ["AAAGAATTCGGG", "TTTTGAATTCAAA", "ATCGATCG"]
for seq in sequences:
if pattern.search(seq):
print(f"EcoRI 부위 있음: {seq}")
컴파일된 패턴 객체는 search(), findall(), sub() 등의 메서드를 직접 호출할 수 있습니다.
플래그
정규표현식의 동작을 조정하는 옵션입니다.
| 플래그 | 의미 |
| re.IGNORECASE / re.I | 대소문자 구분 없이 매칭 |
| re.MULTILINE / re.M | ^, $가 각 줄의 시작/끝에 적용 |
| re.DOTALL / re.S | .이 줄바꿈도 포함 |
seq = "atcgATCG"
matches = re.findall(r"atcg", seq, re.IGNORECASE)
print(matches) # ['atcg', 'ATCG']
# FASTA 파일 파싱 예시: 헤더와 시퀀스를 함께 추출
fasta = ">seq1\nATCGATCG\n>seq2\nGGCCGGCC"
records = re.findall(r">(\S+)\n([ATCG]+)", fasta)
print(records)
# [('seq1', 'ATCGATCG'), ('seq2', 'GGCCGGCC')]
주요 함수 정리표
| 함수 | 반환값 | 주요 용도 |
| re.search(p, s) | 매치 객체 or None | 첫 번째 매칭 위치 탐색 |
| re.match(p, s) | 매치 객체 or None | 문자열 시작부분 패턴 확인 |
| re.findall(p, s) | 리스트 | 모든 매칭 문자열 추출 |
| re.finditer(p, s) | 이터레이터 | 모든 매칭 객체 순회 |
| re.sub(p, r, s) | 문자열 | 패턴 치환 |
| re.split(p, s) | 리스트 | 패턴으로 문자열 분리 |
| re.compile(p) | 패턴 객체 | 패턴 재사용 시 컴파일 |
'BI&Programming-Tools > Python' 카테고리의 다른 글
| [파이썬 기초] 파일 형식 처리 (CSV, TSV, FASTA) (0) | 2026.06.17 |
|---|---|
| [파이썬 기초] 변수 범위와 파일 읽기·쓰기 (0) | 2026.06.17 |
| [파이썬 기초] 함수 정의와 인자 (*args, **kwargs) (0) | 2026.06.16 |
| [파이썬 기초] 람다 함수와 내장 함수, collections 모듈 (0) | 2026.06.16 |
| [파이썬 기초] 반복문 (while, for, comprehension) (0) | 2026.06.16 |
