자연어 처리 - 정규 표현식
정규 표현식
re: 정규 표현식, regular expression, 텍스트 처리
import re
기호 .
.의 갯수만큼의 문자
r = re.compile("a.c")
print(r.search("qwerty"))
None
# r = re.compile("a.c")
print(r.search("abc"))
<re.Match object; span=(0, 3), match='abc'>
print(r.search("abbc"))
None
print(r.search("axc"))
<re.Match object; span=(0, 3), match='axc'>
r2 = re.compile("a..c")
r2.search("abbc")
<re.Match object; span=(0, 4), match='abbc'>
기호 ?
기호 앞의 문자가 0개 이거나 1개 이거나
r = re.compile("ab?c") # a와 c사이에 b가 와야함
print(r.search("abc"))
<re.Match object; span=(0, 3), match='abc'>
print(r.search("abbc"))
None
print(r.search("axc"))
None
print(r.search("ac"))
<re.Match object; span=(0, 2), match='ac'>
기호 *
기호 앞의 문자가 1개 이상
r = re.compile("ab*c")
print(r.search("a"))
None
print(r.search("ac"))
<re.Match object; span=(0, 2), match='ac'>
print(r.search("abc"))
<re.Match object; span=(0, 3), match='abc'>
print(r.search("abbc"))
<re.Match object; span=(0, 4), match='abbc'>
기호 +
기호 앞의 문자가 1개 이상
r = re.compile("ab+c")
print(r.search("ac"))
None
print(r.search("abc"))
<re.Match object; span=(0, 3), match='abc'>
print(r.search("abbc"))
<re.Match object; span=(0, 4), match='abbc'>
기호 ^
기호 뒤의 문자로 시작
r = re.compile("^ab")
print(r.search("ab"))
<re.Match object; span=(0, 2), match='ab'>
print(r.search("aab"))
None
print(r.search("abc"))
<re.Match object; span=(0, 2), match='ab'>
기호 {숫자}
기호 앞의 문자가 숫자 만큼 반복
r = re.compile("ab{2}c")
print(r.search("abc"))
None
print(r.search("abbc"))
<re.Match object; span=(0, 4), match='abbc'>
기호 {숫자1, 숫자2}
기호 앞의 문자가 숫자1 ~ 숫자2까지 반복
r = re.compile("ab{2,4}c")
print(r.search("abc"))
None
print(r.search("abbc"))
<re.Match object; span=(0, 4), match='abbc'>
print(r.search("abbbc"))
<re.Match object; span=(0, 5), match='abbbc'>
print(r.search("abbbbc"))
<re.Match object; span=(0, 6), match='abbbbc'>
print(r.search("abbbbbc"))
None
기호 {숫자, }
기호 앞의 문자가 숫자이상 반복
r = re.compile("ab{2,}c")
print(r.search("abc"))
None
print(r.search("abbc"))
<re.Match object; span=(0, 4), match='abbc'>
print(r.search("abbbc"))
<re.Match object; span=(0, 5), match='abbbc'>
print(r.search("abbbbc"))
<re.Match object; span=(0, 6), match='abbbbc'>
print(r.search("abbbbbc"))
<re.Match object; span=(0, 7), match='abbbbbc'>
기호 [ - ]
문자의 범위 앞-뒤 까지 순서대로 하나 찾음
r = re.compile("[b-e]")
print(r.search("abcdefghi"))
<re.Match object; span=(1, 2), match='b'>
print(r.search("defghijklmn"))
<re.Match object; span=(0, 1), match='d'>
print(r.search("efghijklmn"))
<re.Match object; span=(0, 1), match='e'>
print(r.search("fghijklmn"))
None
기호 [^ ]
뒤의 문자를 제외하고 하나 찾음
r = re.compile("[^abcd]")
print(r.search("a"))
None
print(r.search("abc"))
None
print(r.search("bcd"))
None
print(r.search("abcde"))
<re.Match object; span=(4, 5), match='e'>
print(r.search("efg"))
<re.Match object; span=(0, 1), match='e'>
re.match vs re.search
r = re.compile("ab.")
print(r.match("tttabc")) # match: 처음부터 조사
None
print(r.match("abcttt"))
<re.Match object; span=(0, 3), match='abc'>
print(r.search("tttabc"))# search: 텍스트 전체를 보고 정규식(compile) 조사
<re.Match object; span=(3, 6), match='abc'>
텍스트 분해
text = "자, 이제 match 메서드와 search 메서드를 수행한 결과로 리턴된 match 객체에 대해 알아보자. 앞에서 정규식을 사용한 문자열 검색을 수행하면서 아마도 다음과 같은 궁금증이 생겼을 것이다."
# 파이썬 내장 함수 split
text.split()
['자,',
'이제',
'match',
'메서드와',
'search',
'메서드를',
'수행한',
'결과로',
'리턴된',
'match',
'객체에',
'대해',
'알아보자.',
'앞에서',
'정규식을',
'사용한',
'문자열',
'검색을',
'수행하면서',
'아마도',
'다음과',
'같은',
'궁금증이',
'생겼을',
'것이다.']
# re split 함수: 간단한 상수 문자열 일치로는 충분하지 않은 경우를 위해 설계
re.split(" ", text)
['자,',
'이제',
'match',
'메서드와',
'search',
'메서드를',
'수행한',
'결과로',
'리턴된',
'match',
'객체에',
'대해',
'알아보자.',
'앞에서',
'정규식을',
'사용한',
'문자열',
'검색을',
'수행하면서',
'아마도',
'다음과',
'같은',
'궁금증이',
'생겼을',
'것이다.']
text2 = "안녕 나는+파이썬이야"
re.split("\+", text2)
['안녕 나는', '파이썬이야']
text2.split("+")
['안녕 나는', '파이썬이야']
text3 = '''
이름:Luke Skywarker
전화번호: 02-123-4567
이메일:luke@daum.net
성별:남자
'''
re.findall("\d+", text3) # \d: 숫자
['02', '123', '4567']
text4 = '''“이온빔을 HfO2 기반 강유전체에 조사해 산소 공공을 형성했다”며 “기존의 복잡한 공정과 후처리 과정 없이 이온빔 조사밀도 조절만으로 강유전성을 200% 이상 강화했다”'''
re_res = re.sub("[^a-zA-Z]", ' ', text4) # 대소문자를 제외하고 ' '(공백)으로 치환
re_res
' HfO '
re_res.split()
['HfO']
text5 = "ㅋㅋㅋ so 이따가 시간 돼?ㅠㅠ"
re.compile("[ 가-힣]+").sub("", text5) # 가-힝: 완성형 한글 전체 범위
'ㅋㅋㅋso?ㅠㅠ'
re.compile("[가-힣]+").findall(text5)
['이따가', '시간', '돼']
re.compile("[가-힣]+").sub("", text5).split()
['ㅋㅋㅋ', 'so', '?ㅠㅠ']
s.split() vs. re.split(s)
import re, time, random
def random_string(_len):
letters = "ABC"
return "".join([letters[random.randint(0,len(letters)-1)] for i in range(_len) ])
r = random_string(2000000)
pattern = re.compile(r"A")
start = time.time()
# pattern.split(r)
re.split(pattern, r)
print("re.split : ", time.time() - start)
start = time.time()
r.split("A")
print("내장 split : ", time.time() - start)
re.split : 0.06091761589050293
내장 split : 0.03753256797790527
s = "One:two::t h r e e:::fourth field"
print(re.split(':+', s))
print(s.split(':'))
s = "One:two:2:t h r e e:3::fourth field"
print(re.split('[:\d]+',"One:two:2:t h r e e:3::fourth field"))
['One', 'two', 't h r e e', 'fourth field']
['One', 'two', '', 't h r e e', '', '', 'fourth field']
['One', 'two', 't h r e e', 'fourth field']
내장 split은 추가로 작업이 더 필요하다
ex
search_target = '''Luke Skywarker 021234567 luke@daum.net
다스베이더 070-9999-9999 darth_vader@gmail.com
princess leia 010 2454 3457 leia@gmail.com'''
regex = r'0\d{1,2}[ -]?\d{3,4}[ -]?\d{3,4}'
result = re.findall(regex, search_target)
print("\n".join(result))
021234567
070-9999-9999
010 2454 3457
Reference
- stackoverflow: Python re.split() vs split()
- 프로그래머스: 정규표현식 사용해 보기
댓글남기기