시작에 앞서
해당 내용은 '<Do it! 점프 투 파이썬> 박응용 지음. 이지스 퍼블리싱' 을 토대로 작성되었습니다. 보다 자세한 내용은 해당 서적에 상세히 나와있으니 서적을 참고해 주시기 바랍니다.
이전 내용
정규 표현식 - 전방 탐색 (Lookahead Assertions)
전방탐색이란 일치 항목을 찾을 때 특정 패턴 앞에 오는 다른 패턴을 확인하기 위해 사용되는 고급 정규 표현식 기법이다.
전방탐색 확장구문을 사용하면 암호문처럼 알아보기 어렵게 바뀌나, 전방탐색이 꼭 필요하고 매우 유용한 경우가 있다.
# 전방 탐색
p = re.compile(".+:")
m = p.search("http://google.com")
print(m.group())
▶ 정규식 ".+:"와 일치하는 문자열로 http:를 돌려주었다.
긍정형 전방 탐색, 부정형 전방 탐색
전방탐색에는 긍정(Positive)과 부정(Negative) 두 종류가 있다.
정규식 | 종류 | 설명 |
(?=...) | 긍정형 전방 탐색 | ...에 해당하는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않는다 |
(?!...) | 부정형 전방 탐색 | ...에 해당하는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소비되지 않는다 |
◆ 긍정형 전방 탐색
긍정형 전방 탐색을 사용하여 http:의 결과를 http로 바꾸기
# 긍정형 전방 탐색
p = re.compile(".+(?=:)")
m = p.search("http://google.com")
print(m.group())
▶ 정규식 중 :에 해당하는 부분에 긍정형 전방 탐색 기법을 적용하여 (?=:)으로 변경
이러한 경우 기존 정규식과 검색에서는 동일한 효과를 발휘하나 :에 해당하는 문자열이 정규식 엔진에 의해 소비되지 않아 (검색에는 포함되나, 검색결과에선 제외) 검색 결과에서는 :이 제거된 후 돌려주는 효과가 있다.
다음은 '파일 이름 + . + 확장자'를 나타내는 정규식 '.*[.].*$'이다.
# 긍정형 전방 탐색2
p = re.compile('.*[.].*$') #파일명+확장자
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf")
print(m)
print(n)
print(o)
이 정규식에 확장자가 'bat 인 파일은 제외해야 한다'는 조건을 추가해보면
# bat 확장자 제외
#.*[.][^b].$ #문자 클래스 []안의 ^ 메타 문자는 반대(not) 의미
p = re.compile('.*[.][^b].*$')
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf")
print(m)
print(n)
print(o)
▶ 정규식 '.*[.][^b].$'은 확장자가 b라는 문자로 시작하면 안 된다는 의미로써, bat 뿐 아니라 bar 확장자 파일마저 걸러낸다.
따라서, 이를 수정해야 할 필요가 있다.
# bat 확장자 제외1 : | 메타 문자 사용
#.*[.][^b]..|.[^a].|..[^t])$s
#첫 번째 문자가 b가 아니거나, 두 번째 문자가 a가 아니거나, 세 번째 문자가 t가 아닌 경우
p = re.compile('.*[.][^b]..|.[^a].|..[^t])$s')
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf") #확장자가 2개인 케이스를 포함하지 못함
print(m)
print(n)
print(o)
▶ 정규식 '.*[.][^b]..|.[^a].|..[^t])$s' 은
첫 번째 문자가 b가 아니거나, 두 번째 문자가 a가 아니거나, 세 번째 문자가 t가 아닌 경우를 의미하는데,
이를 수행하면 "sendmail.cf" 처럼 확장자의 문자 개수가 2개인 케이스를 포함하지 못하는 오작동을 일으키게 된다.
그래서 이를 또 수정해야 한다.
# bat 확장자 제외2
# .*[.]([^b].?.?|/[^a]?.?|..?[^t]?)$
# 확장자의 문자 개수가 2개여도 통과
p = re.compile('.*[.]([^b].?.?|/[^a]?.?|..?[^t]?)$')
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf")
print(m)
print(n)
print(o)
이제 확장자의 문자 개수가 2개여도 통과되는 정규식이 만들어졌다.
그러나, 여기에서 bat 파일에, exe 파일도 제외하라는 조건이 추가 된다면 패턴이 매우 복잡해질 것이다.
◆ 부정형 전방 탐색
이러할 때 부정형 전방 탐색을 사용하면 간단하게 처리된다.
# 부정형 전방 탐색
# bat 확장자 제외
p = re.compile('.*[.](?!bat$).*$')
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf")
print(m)
print(n)
print(o)
exe 역시 제외하라는 조건이 추가 되어도 간단히 표현할 수 있다.
# 부정형 전방 탐색
# bat 확장자 제외 + exe 확장자 제외
p = re.compile('.*[.](?!bat$|exe$).*$')
m = p.search("foo.bar")
n = p.search("autoexec.bat")
o = p.search("sendmail.cf")
r = p.search("excel.exe")
print(m)
print(n)
print(o)
print(r)
문자열 바꾸기(feat. sub메서드)
sub 메서드를 사용하면 정규식과 매치되는 부분을 다른 문자로 쉽게 바꿀 수 있다.
sub('바꿀 문자열', '대상 문자열')
# 문자열 바꾸기(feat. sub메서드)
p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes')
▶ blue 또는 white 또는 red 문자열이 colour 라는 문자열로 바뀌었다.
※ 바꾸기 횟수를 제어하려면 세 번째 매개변수로 count 값을 넣으면 된다.
sub('바꿀 문자열', '대상 문자열', count='바꿀 횟수')
# 문자열 바꾸기 + 횟수(count)
p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes', count=1)
▶ 바꾸는 횟수를 1번으로 지정하니, 처음 일치하는 blue만 colour로 바뀌었다.
◆ subn: sub 메서드와 유사하나, 반환 결과를 튜플로 돌려준다.
# subn: 튜플로 반환
p = re.compile('(blue|white|red)')
p.subn('colour', 'blue socks and red shoes')
▶ 변경된 문자열과 변경된 횟수를 튜플로 반환한다.
◆ sub 메서드를 사용할 때 참조 구문 사용하기
sub의 바꿀 문자열 부분에 '\g<그룹 이름>'을 사용하면 정규식의 그룹 이름을 참조할 수 있게 된다.
# sub 매서드, 참조구문
p = re.compile(r"(?P<name>\w+)\s+(?P<phone>(\d+)[-]\d+[-]\d+)")
print(p.sub("\g<phone> \g<name>", "Song 010-5678-1234"))
▶ '이름 + 전화번호'의 문자열을 '전화번호 + 이름'으로 바꿨다.
그룹 이름 대신 참조 번호를 사용해도 된다.
# sub 매서드, 참조 번호
p = re.compile(r"(?P<name>\w+)\s+(?P<phone>(\d+)[-]\d+[-]\d+)")
print(p.sub("\g<2> \g<1>", "Song 010-5678-1234"))
◆ sub 메서드의 매개변수로 함수 넣기
# sub 메서드 - 매개변수에 함수 넣기
def hexrepl(match):
value = int(match.group())
return hex(value)
p = re.compile(r'\d+')
p.sub(hexrepl, "Call 65490 for printing, 49152 for user code.")
▶ hexrepl 함수는 match 객체(위에서 숫자에 매치되는)를 입력으로 받아 16진수로 변환하여 돌려주는 함수이다.
sub의 첫 번째 매개변수로 함수를 사용할 경우 해당 함수의 첫 번째 매개변수에는 정규식과 매치된 match 객체가 입력된다.
그리고 매치되는 문자열은 핫무의 반환 값으로 바뀌게 된다.
Greedy vs Non-Greedy (탐욕법)
정규식에서 Greedy란,
일치하는 것을 최대한 많이 찾으려고 하는 것을 말한다.
# 탐욕법(Greedy) vs Non-Greedy
s = '<html><head><title>Title</title>'
print(len(s))
print(re.match('<.*>', s).span())
print(re.match('<.*>', s).group())
#non-greedy
print(re.match('<.*?>', s).group())
▶ '<.*>' 정규식에서 * 메타문자는 매치할 수 있는 최대한의 문자열인 '<html><head><title>Title</title>' 문자열을 모두 소비해 버린다.
'<html>' 문자열만 소비하기 위해서는 non-greedy 문자인 ? 를 사용하면 된다.
※ non-greedy 문자인 ?는 *?, +?, ??, {m,n}?와 같이 사용하여 가능한 한 가장 최소한의 반복을 수행하도록 도와주는 역할을 한다.
전체 코드
'[파이썬 Projects] > <파이썬 기초>' 카테고리의 다른 글
[파이썬] 로또 번호 생성 프로그램 만들기 (0) | 2024.07.03 |
---|---|
[파이썬] konlpy 설치 관련 JVM 오류 해결 (1) | 2024.07.03 |
[파이썬] 파이썬기초: 정규 표현식(Regular Expressions) - 3 (0) | 2024.06.22 |
[파이썬] 파이썬기초: 정규 표현식(Regular Expressions) - 2 (0) | 2024.06.22 |
[파이썬] 파이썬기초: 정규 표현식(Regular Expressions) - 1 (0) | 2024.06.21 |