세상을 더 편리하게
728x90

메타 문자

정규 표현식에서 제공하는 특정 기능을 수행하는 문자들이 있다. 이를 메타 문자라고 한다.

메타 문자를 이용하면 더 아름다운 정규 표현식이 가능하다.

|

| 는 or 와 같은 의미를 갖는다.

import re
p = re.compile("Crow|Servo")
m = p.match('CrowHello')
print(m.group()) #Crow

^

^는 문자열 맨 처음과 일치함을 의미한다.

import re
p = re.compile("^Life")
m1 = p.match('Life is too short')
m2 = p.match("Beautiful Life")
print(m1) #<re.Match object; span=(0, 4), match='Life'>
print(m2) #None

※ 문자 클래스 [ ] 에 들어간 [^] 는 Not 을 의미한다. 서로 의미가 다르니 주의하자.

$

$는 ^의 반대말로 문자열 끝과 일치함을 의미한다.

\A

문자열 처음과 매치됨을 의미한다. ^와 비슷해 보이지만 다중 문자열에서는 의미가 다르다.

다중 문자열 속 ^는 각 줄을 체크하지만 \A는 처음만 매치됨을 비교한다.

\Z

문자열 끝과 매치됨을 의미한다. 이것도 역시 마찬가지로 $와 유사하지만

다중 문자열 속에 $는 각 문장 맨 끝을, \Z 마지막만 매치됨을 비교한다.

\b

import re
p = re.compile(r'\bclass\b')
m = p.search('no class at all')
m2 = p.search('subclass')
print(m.group()) # class
print(m2) # None

\b 는 단어를 구분할 때 쓰인다. 다음의 단어가 존재하는지 찾아보는 것인다.

이 때 \b 를 사용하기 위해서는 r 를 컴파일에 써야한다. r의 역할은 뒤에 오는 문자열을 있는 그대로 파악하라는 뜻이다.

일반 문자열에서는 \b 는 백스페이스를 뜻하기에 구분하기 위해서 r 를 사용한다.

\B

\B는 \b 와 반대이다. class 를 포함하는 단어가 있는지 찾아보는 것이다.

import re
p = re.compile(r'\Bclass\B')
m = p.search('no class at all')
m2 = p.search('subclass')
m3 = p.search('declassified')
print(m) # None
print(m2) # None
print(m3) # <re.Match object; span=(2, 7), match='class'>

class 양쪽으로 \B가 둘러싸고 있기에 class가 중간에 포함된 단어만 매치가 됨을 알 수 있다.

그룹핑

그룹핑은 정규 표현식에서 ( ) 소괄호로 표시된 것을 의미한다.

import re
p = re.compile(r'(\w+)\s+\d+[-]\d+[-]\d+')
m = p.search("park 010-1234-1234")
print(m.group(1)) #park

위에서 보면 \w+ 즉 영단어+숫자로 이루어진 단어이다. 1번째 소괄호에 있으므로 group(1) 을 출력하면

이에 맞는 문자열이 출력이 된다.

group(0) = 전체 문자열

group(n) = n번째 그룹핑[n번째 소괄호]

import re
p = re.compile(r'(\w+)\s+\d+[-]\d+[-](\d+)')
m = p.search("park 010-1234-5678")
print(m.group(2)) #5678

위의 코드는 전화번호의 마지막 4자리 번호만 얻는 코드이다. 

그룹핑 재참조

정규 문자열에서 그룹핑한 것은 그룹핑 번호로 재참조 될 수 있다.

import re
p = re.compile(r'(\w+)\s+\1')
m = p.search('I want pork pork')
print(m.group()) #pork pork

위에서 보면 \1 이 사용된 것을 볼 수 있다. 그룹핑 1번째는 (\w+) 이다. 즉 반복되는 단어가 2번 나온다면 매치한다는 것이다. 

그렇기에 pork pork 가 일치하기에 매치 된 것을 알 수 있다.

 

그룹핑 이름 붙이기

그룹핑에서 이름은 ?P<이름>의 형식으로 붙일 수 있다.

import re
p = re.compile(r'(\w+)\s+\d+[-]\d+[-](?P<back>\d+)')
m = p.search("park 010-1234-5678") 
print(m.group('back')) #5678

위에서 보면 back 이라는 그룹의 이름이 붙었기에 back 으로 그룹을 호출 할 수 있다.

import re
p = re.compile(r'(?P<meat>\w+)\s+(?P=meat)')
m = p.search('I want pork pork')
print(m.group()) #pork pork

또한 그룹핑에 이름은 붙인 것은 이름으로 재참조할 수 있다.

전방 탐색

긍정형 전방 탐색

말이 조금 어렵지만 다음 조건을 포함한 검색 이라고 생각하면 조금 쉬울 것같다.  

https://slowsure.tistory.com/  이 블러그의 주소를 예를 들어보자.

나는 https:// 에서 : 이전에만 검색하고 싶다. 

import re
p = re.compile('.+(?=:)')
m = p.search('https://slowsure.tistory.com/')
print(m.group()) #https

(?=원하는 문자열) 의 형식을 긍정현 전방탐색이라고 한다.

부정형 전방 탐색

부정형 전방탐색은 검색은 했는데 예외를 두고 싶을 때 사용한다.

파일 확장자에서 pdf 파일은 제외하고 싶다.

import re
file = """
일번.pdf
이번.exe
삼번.txt
사번.ppt
"""
p = re.compile('.*[.][^p].*$',re.MULTILINE)
m = p.search(file)
print(m.group()) #이번.exe

pdf 파일은 안정적으로 제거 된 것 같다 하지만 문제가 있다. ppt 확장자도 p로 시작하는 확장자이기에 같이 제거가 되어버렸다.

이럴 때 사용하는 것이 부정형 전방탐색이다.

import re

file = """
일번.pdf
이번.exe
삼번.txt
사번.ppt
"""
p = re.compile('.*[.](?!pdf$).*', re.MULTILINE)
m = p.findall(file)
print(m)  # ['이번.exe', '삼번.txt', '사번.ppt']

(?!문자열) 을 사용하면 문자열에 해당하는 것만 예외처리가 된다.

import re

file = """
일번.pdf
이번.exe
삼번.txt
사번.ppt
"""
p = re.compile('.*[.](?!pdf$|exe$).*', re.MULTILINE)
m = p.findall(file)
print(m) # ['삼번.txt', '사번.ppt']

| 메타 문자를 사용해서 예외사항을 추가 할 수 있다.

문자열 바꾸기

import re
str = 'blue socks and red shoes'
p = re.compile('(blue|white|red)')
m = p.sub('color',str)
print(m) #color socks and color shoes

위의 코드를 보면 blue가 color로 바뀌는 것을 알 수 있다.

sub( 바뀔 문자열, 바꾸고 싶은 문자열) 을 이용하면 내가 원하는 부분만 문자를 바꿀 수 있다.

※subn 함수도 sub와 문법은 같으나 리턴되는 값이 튜플로 (바뀐 문자열, 바뀐 횟수)를 반환한다.

import re
str = 'blue socks and blue shoes'
p = re.compile('(blue|white|red)')
m = p.sub('color',str, count=1)
print(m) #color socks and blue shoes

위 코드는 sub( 바뀔 문자열, 바꾸고 싶은 문자열, count = n) 을 사용하면 앞에서 부터 n번 만큼만 문자를 대체 할 수 있다.

sub 함수 사용시 그룹핑 네임 사용하기

import re

p = re.compile( r'(?P<name>\w+)\s+(?P<number>(\d+)[-](\d+)[-](\d+))')
m = p.sub('\g<number> \g<name>', 'park 010-1111-2222')
print(m) #010-1111-2222 park

sub에서 \g<이름> 을 사용해서 정규 문자식에서 사용한 그룹핑 네임을 이용할 수 있다.

728x90
profile

세상을 더 편리하게

@쵱니

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!