반응형
정규 표현식(Regular Expressions)이란?
- 정규식은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용한다.
정규 표현식을 지원하는 re 모듈
>>> import re
>>> p = re.compile('[a-z]+')
>>> m = p.match("python")
# 축약 가능
>>> m = re.match('[a-z]+', "python") # 컴파일 match메서드 한번에 수행 가능
정규식을 이용한 문자열 검색
- match(): 문자열 처음부터 정규식과 매치되는지 조사
# 매치될 때는 match 객체를 돌려주고, 매치되지 않을 때는 None
>>> m = p.match("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
>>> m = p.match("3 python")
>>> print(m)
None
- search():문자열 전체를 검색해 정규식과 매치되는지 조사
>>> m = p.search("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
>>> m = p.search("3 python")
>>> print(m)
<re.Match object; span=(2, 8), match='python'>
- findall(): 정규식과 매치되는 모든 문자열을 리스트로 돌려줌
>>> result = p.findall("life is too short")
>>> print(result)
['life', 'is', 'too', 'short'] # 정규식과 매치해서 리스트로 돌려줌
- finditer(): 정규식과 매치되는 모든 문자열을 반복 가능한 객체로 돌려줌
- findall과 finditer는 동일하지만 그 결과로 findall은 리스트 finditer은 반복 가능한 match 객체를 돌려준다.
>>> result = p.finditer("life is too short")
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result: print(r)
...
<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>
Match 객체의 메서드
- match와 search 메서드를 수행한 결과로 돌려준 match 객체
>>> m = p.match("python")
>>> m.group() # group() 매치된 문자열 반환
'python'
>>> m.start() # start() 매치된 문자열의 시작 위치 반환
0 # match 메서드는 항상 시작부터 조사하므로 항상 0
>>> m.end() # end() 매치된 문자열의 끝 위치 반환
6
>>> m.span() # span() 매치된 문자열의 (시작, 끝) 튜플 반환
(0, 6)
>>> m = p.search("3 python")
>>> m.group()
'python'
>>> m.start() # start 메서드는 문자열 전체를 조사
2
>>> m.end()
8
>>> m.span()
(2, 8)
컴파일 옵션
DOTALL(S)
- 메타문자 . 이 줄바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 한다.
보통 여러 줄로 이루어진 문자열에서 \n에 상관없이 검색할 때 많이 사용한다.
# 메타문자 . 이 원래 줄바꿈 문자 \n를 제외한 모든 문자와 매치
>>> import re
>>> p = re.compile('a.b')
>>> m = p.match('a\nb')
>>> print(m)
None
# \n 도 포함하여 모든 문자와 매치
>>> p = re.compile('a.b', re.DOTALL) # re.S 약어도 가능
>>> m = p.match('a\nb')
>>> print(m)
<re.Match object; span=(0, 3), match='a\nb'>
IGNORECASE(I)
- 대소문자에 관계없이 매치할 수 있도록 한다.
>>> p = re.compile('[a-z]+', re.I) # re.IGNORECASE와 동일
>>> p.match('python')
<re.Match object; span=(0, 6), match='python'>
>>> p.match('Python')
<re.Match object; span=(0, 6), match='Python'>
>>> p.match('PYTHON')
<re.Match object; span=(0, 6), match='PYTHON'>
MULTILINE(M)
- 여러줄과 매치할 수 있도록 한다.
- ^은 문자열 처음, $ 은 문자열의 마지막을 의미
- ^python인 경우 문자열의 처음은 항상 python으로 시작해야 매치되고, 만약 정규식이 python$이라면 문자열의 마지막은 항상 python으로 끝나야 매치된다
# 원래 한 줄만 매치
>>> import re
>>> p = re.compile("^python\s\w+") # python 문자열로 시작 + 여백 + 단어
...
>>> data = """python one
>>> life is too short
>>> python two
>>> you need python
>>> python three"""
...
>>> print(p.findall(data))
['python one'] # ^ 때문에 python 사용한 첫줄만 매치
# Multiline 옵션으로 여러줄 매치 - ^가 문자열 전체가 아닌 각 줄의 처음 의미
>>> import re
>>> p = re.compile("^python\s\w+", re.MULTILINE)
...
>>> data = """python one
>>> life is too short
>>> python two
>>> you need python
>>> python three"""
...
>>> print(p.findall(data))
['python one', 'python two', 'python three']
VERBOSE(X)
- verbose 모드를 사용하면 문자열에 사용된 여백들이 컴파일할 때 제거되며, 줄 단위로 #기호를 사용하여 주석문을 작성할 수 있다. (단 [ ] 안에 사용한 여백는 제외)
# 원래 복잡
>>> charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
# verbose모드 - 여러줄 + 주석
charref = re.compile(r"""
&[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
\ 백슬래시
- \ 문자가 문자열 자체임을 알려 주기 위해 백슬래시 4개를 사용하여 이스케이프 처리를 해야 하므로 간단하게 Raw string 처리 해줌
# 백슬래시를 문자열 취급하려면 \\\\ 백슬래시 4번 써줌
>>> p = re.compile('\\\\section') # \s가 여백으로 해석되므로
# 복잡하므로 raw string 처리
>>> p = re.compile(r'\\section')
전방 탐색 확장 구문
긍정형 전방 탐색((?=...))
- ... 에 해당되는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않는다 - 검색에는 포함되지만 검색 결과에는 제외됨.
# 원래
>>> p = re.compile(".+:")
>>> m = p.search("http://google.com")
>>> print(m.group())
http:
# 기존:를 긍정형전방탐색을 적용하여 (?=:)로 변경
>>> p = re.compile(".+(?=:)")
>>> m = p.search("http://google.com")
>>> print(m.group())
http
부정형 전방 탐색((?!...))
- ...에 해당되는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소비되지 않는다.
# 원래
.*[.].*$ # 파일 이름 + . + 확장자
# 특정 확장자 파일 제외
.*[.](?!bat$).*$ # 확장자 bat 아닌 경우에만 통과
.*[.](?!bat$|exe$).*$ # 확장자 bat, exe 아닌 경우에만 통과
매치되는 문자열 바꾸기
sub 메서드(바꿀 문자열, 대상 문자열)
# sub메서드로 바꾸기
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
# 바꾸기 횟수 제한
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
- sub의 바꿀 문자열 부분에 \g<그룹이름>이나 \g<참조번호> 를 사용하면 정규식의 그룹 이름을 참조할 수 있게 된다.
# 이름 전화번호 순서 바꾸기
>>> p = re.compile(r"(?P<name>\w+)\s+(?P<phone>(\d+)[-]\d+[-]\d+)")
>>> print(p.sub("\g<phone> \g<1>", "park 010-1234-1234"))
010-1234-1234 park
- sub의 첫 번째 매개변수로 함수를 사용할 경우 해당 함수의 첫 번째 매개변수에는 정규식과 매치된 match 객체가 입력된다. 그리고 매치되는 문자열은 함수의 반환 값으로 바뀌게 된다.
# sub 메서드의 첫번째 매개변수로 함수도 가능
>>> def hexrepl(match): # 16진수로 변환 함수
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
subn 메서드(바꿀 문자열, 대상 문자열)
- sub와 동일한 기능을 하지만 반환 결과를 튜플로 돌려줌.
- 돌려준 튜플의 첫 번째 요소는 변경된 문자열이고 두 번째 요소는 바꾸기가 발생한 횟수
>>> p = re.compile('(blue|white|red)')
>>> p.subn( 'colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
Greedy과 Non-Greedy
- 보통 메타 문자는 매우 탐욕(Greedy)스러워서 매치할 수 있는 최대한의 문자열을 모두 소비
- Non-greedy 문자인 ?는 *?, +?, ??, {m,n}?와 같이 사용하는데, 가능한 최소한의 반복을 수행하도록 도와주는 역할.
>>> s = '<html><head><title>Title</title>'
# Greedy - 가능한 최대로 매치
>>> print(re.match('<.*>', s).span())
(0, 32)
>>> print(re.match('<.*>', s).group())
<html><head><title>Title</title>
# Non-greedy - 가능한 최소한으로 매치
>>> print(re.match('<.*?>', s).group())
<html>
Reference
반응형
'Data > Python' 카테고리의 다른 글
[Python] 파이썬 주피터노트북 단축키 모음 (0) | 2023.06.18 |
---|---|
[Python] 파이썬 정규표현식 - 메타문자들, 그룹핑 (0) | 2023.06.18 |
[Python] 파이썬 자주 쓰는 외장함수들 (1) | 2023.06.18 |
[Python] 파이썬 자주 쓰는 내장함수들 (0) | 2023.06.17 |
[Python] 파이썬 오류 예외 처리 - try, except, finally (0) | 2023.06.17 |