필자는 "정규 표현식"을 이 책 《점프 투 파이썬》에 포함시켜야 할지 오랜시간 고민했다. 왜냐하면 정규 표현식은 꽤 오랜 기간 코드를 작성해 온 프로그래머라도 잘 모를 수 있는 고급 주제여서 초보자를 대상으로 하는이 책에는 어울리지 않을 수 있기 때문이다.
하지만 정규 표현식을 배워 익히기만 하면 아주 달콤한 열매를 맛볼 수 있다. 그래서 파이썬 하우투(docs.python.org/3.7/howto/regex.html)를 참고하여 그곳에서 소개하는 수준의 내용만이라도 독자들이 이해하고 사용할 수 있도록 노력했다. 여러분이 정규 표현식을 잘 다루면 파이썬 외에 또 하나의 강력한 무기를 얻게 되는 것이다.
다시 말하지만 프로그래밍 입문자가 이해하기에는 어려운 내용이니 부담 갖지 말고 편하게 읽어 주기 바란다.
https://wikidocs.net/1669
나도 몇 달 전, 파이썬 공부를 시작하며 정규식 부분은 제외하고 했었다.
지금 정규식을 하는 건 내가 모든 걸 개발할 수 있는 그런 능력이 돼서 그런게 아니라,
저자분의 말씀대로, 나중에 잘 다루면 나에게 강력한 무기가 될 수 있기 때문이다.
복잡한 문자열 처리 시, 사용하는 기법
파이썬 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용
정규표현식에서 사용하는 메타문자에는 다음과 같은 것이 있다
. ^ $ * + ? { } [ ] \ | ( )
메타 문자란 원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자
해당 메타문자로 만들어진 정규식은 "[]사이의 문자들과 매치" 라는 의미
정규표현식이 [abc]라면 "a,b,c 중 한 개의 문자와 매치"를 뜻함
예를 들어, 문자열 "a","before","dudue"가 위 정규식과 어떻게 매치되는가?
"a","before"는 정규식과 일치하는 문자인 a,b가 있으므로 매치
"dudue"는 a,b,c 중 포함하는 게 없으므로 매치x
[] 안의 두 문자 사이에 '-'를 사용하면 범위를 뜻함
[a-c] = [abc][0-5] = [0112345][a-zA-Z] = 알파맛 모두
[0-9] = 숫자
반대로 []안의 걸 제외하고 싶다면 '^'를 붙이면 된다
[^0-9] = 숫자를 제외한 문자
DOT은 \n을 제외한 모든 문자와 매칭됨을 의미
a.b = "a+모든문자+b"
즉, a와 b라는 문자 사이에 어떤 문자가 들어가도 모두 매치된다는 의미이다.
예를 들어 "aab", "a0b", "abc"가 위 정규식과 어떻게 매치되는가?
"aab","a0b"는 각각 'a','0'이 모든 문자를 의미하는 '.'과 일치
"abc"는 문자사이에 어떤 문자라도 하나는 있어야 한다는 정규식에 위배
근데 만약 a[.]b는?
나는 문자어떤것이든 오면 되는 줄 알았는데,
말 그대로 '.'을 의미해서 "a.b"와 매칭된다고 한다
정규식 ca*t 는 a가 0부터 무한대로 반복될 수 있다는 의미이다.
문자열이 'ct', 'cat', 'caaat'가 위 정규식과 어떻게 매치되는가?
'ct' : 'a'가 0번 반복되어 매치
'cat': 'a'가 0번 이상 반복되어 매치(1번 반복)
'caaat': 'a'가 0번 이상 반복되어 매치(3번 반복)
정규식 ca*t 는 a가 1부터 무한대로 반복될 수 있다는 의미이다.
문자열이 'ct', 'cat', 'caaat'가 위 정규식과 어떻게 매치되는가?
'ct' : 'a'가 0번 반복되어 매치 안 됨
'cat': 'a'가 0번 이상 반복되어 매치(1번 반복)
'caaat': 'a'가 0번 이상 반복되어 매치(3번 반복)
정규식 ca{mn,}t 는 a가 2-5회로 반복될 수 있다는 의미이다.
문자열이 'ct', 'cat', 'caaat'가 위 정규식과 어떻게 매치되는가?
'ct' : 'a'가 0번 반복되어 매치 안 됨
'cat': 'a'가 0번 이상 반복되어 매치 안 됨(1번 반복)
'caaat': 'a'가 2번 이상 반복되어 매치(3번 반복)
정규식 ab?c 의미는 다음과 같다
b는 있어도 되고 없어도 된다
문자열이 'ab', 'abc'가 위 정규식과 어떻게 매치되는가?
둘다, 'b'가 0-1번 사용되어 매치
re : regular expression
p = re.compile('ab*')
import re 이렇게 선언해주면 되며, 위 코드에서 p는 ab*을 컴파일한다.
Method는 match(), search(), findall(), finditer()가 있다.
각 method별로 목적을 말하면
match
문자열의 처음부터 정규식과 매치되는지 조사
search
문자열 전체를 검색하여 정규식과 매치되는지 조사
findall
정규식과 매치되는 모든 문자열(substring)을 리스트로 돌려준다
finditer
정규식과 매치되는 모든 문자열을 반복가능한 객체로 돌려준다
match, search는 정규식과 매치될 때 match 객체를 돌려주고,
매치안되면 None을 돌려준다
간단하게 아래 식으로 시작해보자
import re
p = re.compile('[a-z]+')
m = p.match("python")
print(m)
하면 python이란 문자열이 정규식에 부합하므로 나온 결과가 아래 한 줄이고
<re.Match object; span=(0, 6), match='python'>
m = p.match("3 python")
print(m)
하면 3은 문자가 아니기 때문에 None이 출력된다
그래서 match의 결과에 따라 돌려주기 위해 if문을씀
import re
p = re.compile('[a-z]+')
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
이렇게 돌리면 결과는 string을 나옴
import re
p = re.compile('[a-z]+')
m = p.search('python')
search와 match 모두 정규식과 매치된다면 match를 돌려주기 때문에,
결과는 match로 실행했을 때와 동일하다.
다만,
m = p.search("3 python")
print(m)
search는 문자열 전체를 검색하기 때문에, 'python'과 매치된다
정규식과 모든 매치되는 문자열을 list로 돌려주기 때문에
import re
p = re.compile('[a-z]+')
result = p.findall("life is too short")
print(result)
하면 저 문구가 나온다.
다만 result = p.findall("life is too 3 short")
일 경우, 3은 정규식에 매칭되지 않아서 리스트에 포함되지 않고
life is too short가 리스트로 되어 0~3번을 자리하고 있다
result = p.findall("life is too short")
설정 시 result를 반복가능한 객체로 돌려준다.
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=(13, 18), match='short'>
각 문자열의 자리수가 어디부터 어디까지인지 알려줌
method는 group(), start(), end(), span()으로 구성되어 있다
group() : 매치된 문자열을 돌려준다
start() : 매치된 문자열의 시작 위치를 돌려준다
end() : 매치된 문자열의 종료 위치를 돌려준다
span() : 매치된 문자열의 (시작,끝)에 해당하는 튜플을 돌려준다
p = re.compile('[a-z]+')
m=p.match("python")
print(m.group())
print(m.start())
print(m.end())
print(m.span())
결과
python
0
6
(0, 6)
문자열이 '3 python'이라면?
python
2
8
(2, 8)
이렇게 바뀌는 걸 확인할 수 있음