NLP-writing

2 정규표현식을 익히자 Regular Expression with Python

summerorange 2023. 3. 20. 17:15
반응형
   목차
1. NLP 란 (링크 : https://summerorange.tistory.com/entry/1-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C)

2. 정규표현식을 익히자 
3. 확률
4. 선형대수
5. 미적분
6. 텍스트 전처리
7. 분류
8. 텍스트 유사도
9. Bert & Gpt
10. ChatBot

 

한줄평: Text처리에는 정규표현식이 쵝오👍

정규표현식이란?

Python에서 원하는 텍스트만 추출할 때 꼭 써야 하는 게 정규표현식입니다. 파이썬 뿐만 아니라 JAVA, C, JavaScript, PHP, Rust, C++, 에도 지원합니다. 파이썬 이외에도 자주 썼던 경우는, 저 같은 경우는 vim입니다. vim에서 텍스트 대체는 생각보다 자주 쓸 일이 있어서 정규 표현식으로 짜서 쓰는 경우가 있습니다.

정규 표현식을 도대체 왜 쓰는지?

정규 표현식의 장점이란 강력한 텍스트 찾기 기능입니다.

파이썬 코드로 텍스트 대체할 때, 리스트 구조 혹은 딕셔너리 구조 만들어서 원소 바꾸기를 한다? 이렇게 하는 순간 코드도 길어지고... 그 for ~ in range 루프의 메모리 잡아먹기ㅋㅋㅋ큐ㅠㅠ 정규 표현식으로 짜면 코드가 간결해집니다.

또한, 컴파일 하면 속도도 빨라지는데, 요즘 성능의 컴퓨터로는 속도가 빨라지는지 솔직히 모르겠ㅋㅋㅋ 데이터가 작아서 그런지 0.000001139 정도 빨라지는 것 같은데... 만약 데이터가 엄청나게 크다면 이 속도는 유의미하게 중요합니다.

컴파일 했을 경우(66.3nsec)와 하지 않았을 경우(197nsec)

핫하게 사용되는 영역

문자열 데이터의 처리 String Processing, 웹 스크래핑 Web Scraping, 검색 엔진 Search Engines,

사실 전반적으로 사용됩니다. 사용자 인풋 값은 string 형태일 때가 많으니 제약조건을 걸 때에도 사용하고, 서버 관련 코드 짤 때에도 리눅스 vim 등등 텍스트 대체에서도 정규표현식이 필요합니다.

 

정규 표현식의 단점?

한줄평: 문법이 어렵다.

일단 외계어 같습니다. 어셈블리 보다 더 모호한 것 같다는 느낌이 있습니다. 힘들게 익혀두어도 안 쓰면 잊어먹습니다.

 

정규 표현식은 언제부터 쓰였는지?

상당히 역사가 깊습니다.

컴퓨터의 언어의 발전이 B언어 -> C언어 -> C++ ... 컴퓨터 언어와 같이 태어났다고 보시면 됩니다. 1950년대 정도는 지금과 같은 개인용 컴퓨터는 없었고, 기업이나 국가에서만 사용되는 거대한 서버용 컴퓨터가 맨 처음 등장했습니다. 즉 UNIX 컴퓨터. UNIX에서 텍스트 처리를 할 때 사용한 개념이 정규 표현식 입니다. 특정 문자열이 들어있는 폴더나 파일을 찾아줘. 해당 내용을 찾아줘. 등 텍스트 검색할 때 사용했습니다. 그 땐 이렇게 사용자가 보기 편한 화면이 없었으니까, 텍스트 검색이 필수 였습니다. 대부분 Command Line...

이 때 두 가지 구문을 사용했는데 하나는 POSIX, 다른 하나는 Perl.

Perl 구문을 현재 Java, Javascript, Python , .NET 등에서 채택해서 사용하고 있습니다. 예를 들면 .+, .* 

 

Python 에서 정규표현식이란?

Python에는 정규표현식 내장 라이브러리 (re)가 있습니다. 

import re

data = "오늘은 비가 내립니다"
re.search("^오늘은.*니다$", data)
# 결과
# <re.Match object; span=(0, 11), match='오늘은 비가 내립니다'>

다음과 같이 re를 import해서 사용할 수 있습니다.

 

Python 정규표현식 함수

사용 예시들은 모두 컴파일을 하지 않은 간단한 예시입니다.

함수 사용 예시 리턴 값
search # 패턴 검색
text = "The quick brown fox jumps over the lazy dog"
pattern = "fox"
result  = re.search(pattern, text)
#<re.Match object; span=(16, 19), match='fox'>
문자열에서 해당하는 객체 리턴
findall # 여러 패턴 일치
text = "apple, banana, cherry"
patterns = ["apple", "cherry"]
results = re.findall("|".join(patterns), text)
# ['apple', 'cherry']
문자열에서 해당하는 텍스트 리스트로 리턴
sub # 패턴 대체
text = "The quick brown fox jumps over the lazy dog"
pattern = "fox"
new_text = re.sub(pattern, "cat", text)
# The quick brown cat jumps over the lazy dog
문자열에서 해당 문자열을 다른 문자열로 대체함
split
#스페이스 기준 자르기
txt = "여기 스페인에 비가 내립니다"
x = re.split("\s", txt)
print(x)
# ['여기', '스페인에', '비가', '내립니다']
문자열에서 해당하는 매치를 split한 리스트를 리턴

함수 중, 자주 쓰는 건 finall, sub 이 두 개이다. 

 

Python 정규표현식 메타문자(MetaCharacters)

메타문자(MetaCharacters) 는 특별한 의미를 가진 문자를 의미함.

메타문자 의미 예시
[] 문자열 셋트 [a-zA-Z] : 영어 문자
[0-9] : 숫자
[abc] : a, b, c중에 하나라도 있으면 매칭
[^abc] : a, b, c는 제외하고 알파벳 매칭
[0-5][6-9] 06, 49 등의 두 자리를 매칭
[ㄱ-ㅎ] 한글
[+] : 정말로 + 를 매칭해줌 "5+8=13" 일 경우 ['+'] 리스트로 반환 받을 수 있음          예시: (re.findall("[+]", "5+8=13")
\ 또다른 메타 의미가 있는 문자를 의미함 \s : 띄어쓰기
. 모든 문자를 의미 호.이 : 호랑이, 호랭이, 호윤이, 호돌이, 호둘이, 호순이, 호호이, 호.이, 호@이 모두 포함
^ 시작하는 문자 ^Hello : Hello로 시작하는 문자. hello는 안됨. 대소문자 구분
$ 끝나는 문자 world$ : world로 끝나는 문자. 역시 대소문자 구분
* 0 이상
x = re.findall("he.*", "he")
x
#['he']
+ 1 이상
x = re.findall("he.+", "hello World")
x
#['hello World']
? 0 또는 1번
x = re.findall("he.?", "hello")
x
#['hel']
{} 구체적인 숫자를 설정할 수 있음
x = re.findall("he.{3}", "hello")
x #['hello']
| 또는
x = re.findall("그|그녀", "그는 현재 스페인 마드리드에 있다")
x #['그']
() 그룹  

 그룹의 경우 예시는 하단에,

그룹의 바깥쪽이 먼저, 안쪽이 그 다음으로 숫자가 매겨지며.

data = '''
123456-1234567
12345-123456
123456-12345678
990313-3900000
'''
pattern = re.compile(r"((\d{6})[-]\d{7})")
result = pattern.search(data)

print(result.group(1)) # 123456-1234567

print(result.group(2)) #123456

다음과 같다.

result2 = pattern.findall(data)

# ---- result below -----
result2
[('123456-1234567', '123456'),
 ('123456-1234567', '123456'),
 ('990313-3900000', '990313')]

특수 배열 문자(Special Sequence)

메타 문자 중에 \ 라는 표시는 특수 문자를 가리킵니다.

특히 검색할 때 \speak 등으로 검색하면 \speak 을 검색하는 것이 아니라 \s를 특별 문자로 취급받아서 peak 가 검색됩니다. 이런 점을 방지하기 위해서 r'\\speak' 등으로 앞에 r이 붙으며.  r은 "raw string"을 의미합니다. 즉, r'\\speak'는  '\speak' 를 검색해달라는 뜻

특수 배열문자 리스트는 하단에...

문자 의미 예시
\A 문자열 시작하는 문자를 의미 "\A그녀는"
\b 문자열에서 시작하거나 끝나는 단어를 의미 x = re.findall(r"\b시장", "예전에 당나귀가 나그네와 함께 여행을 떠났었는데, 둘은 전에 시장에서")
\B 문자열에서 시작하거나 끝나는 단어가 아닌 것 x = re.findall(r"\B전에", "예전에 당나귀가 나그네와 함께 여행을 떠났었는데, 둘은 전에 시장에서")
x = re.findall(r"\B장에", "예전에 당나귀가 나그네와 함께 여행을 떠났었는데, 둘은 전에 시장에서")
\d 숫자를 의미 x = re.findall("\d", "오늘 산 초밥은 1,5000원")
# ['1', '5', '0', '0', '0']
\D 숫자가 아닌 것을 의미 x = re.findall("\D", "오늘 산 초밥은 1,5000원")
# ['오', '늘', ' ', '산', ' ', '초', '밥', '은', ' ', ',', '원']
\s 공백문자를 의미 x = re.findall("\s", "오늘 산 초밥은 1,5000원")
# [' ', ' ', ' ']
\S 공백문자가 아닌 것
x = re.findall("\S", "오늘 산 초밥은 1,5000원")
# ['오', '늘', '산', '초', '밥', '은', '1', ',', '5', '0', '0', '0', '원']
\w 문자나 숫자를 의미
x = re.findall("\w", "오늘 산 초밥은 1,5000원")
# ['오', '늘', '산', '초', '밥', '은', '1', '5', '0', '0', '0', '원']
\W 문자나 숫자가 아닌 것
x = re.findall("\W", "오늘 산 초밥은 1,5000원")
# [' ', ' ', ' ', ',']
\Z 문자열에서 끝나는 문자를 의미
x = re.findall("원\Z", "오늘 산 초밥은 1,5000원")
# ['원']
\\ 일반 문자 \ 와 매칭
매타 문자가 아니라는 걸 표기

이 중에서 실제로 자주 사용하는 것은,

\d, \s, \w 가 되겠네요. 

주의해야 할 건 \s .... 자꾸 string이라고 착각하게 되어서ㅋㅋㅠㅠ 헷갈리는... 공백문자... 

 

Python 정규표현식 컴파일 및 옵션

지금까지 예제에선 정규표현식을 컴파일 한 후에 함수를 사용하는 것보다, 컴파일 없이 사용했습니다. 만약 여러 가지 텍스트를 같은 패턴으로 한 번에 컴파일을 하면 좀 더 빨라진다는 이점이 있습니다.

컴파일 옵션 (= 축약 코드) 의미 예시
re.DOTALL (= re.S) 줄바꿈 문자 등도 포함
data = '''오늘은 참 바쁜 하루였다
어제까지만 해도 주말이었는데, 눈을 떠
보니 오늘이 왔다
어제오늘내일'''
pattern = re.compile('다.어', re.S)
result = pattern.findall(data)
# ['다\n어', '다\n어']
re.IGNORECASE (= re.I) 대소문자 구분 없음
data = '''Hello, Eveyone
It is nice to Meet You Again
Today we are going to discuss about Global Climate Change
and I want you to discuss
Any Opion is Okay Just say what you want
Is it Okay?'''
pattern = re.compile('you', re.I)
result = pattern.findall(data)
# ['You', 'you', 'you']
re.MULTILINE (= re.M) ''' 문자 문자 문자'''와 같이 여러 라인이 있는 경우 고려
pattern = re.compile("^It\s\w+", re.M)
result = pattern.findall(data)
# ['It is'] 
re.VERBOSE (= re.X) 정규식에 줄 단위의 주석을 달아서 만들 수 있음
a = re.compile(r"""\d+ # the integral part
\. # the decimal point
\d* # some fractional digits""", re.X)
result = a.findall("000.324 는 23.123 의 x축과 y축의")
# ['000.324', '23.123']

 

정규표현식에서 Greedy 방법을 제어

Greedy 방법이란, 뜻 그대로 탐욕스러운 찾는 방법인데 조금이라도 맞는다면 정보를 다 긁어모으는 방법을 그리디 방법이라고 합니다. 

. (마침표) -> 모든 문자를 의미

? -> 끝을 의미함

예를 들어, [1. 단어] 하는 이유는 ~ [2. 단어단어] 사회생활을 하면서 ~ [3. 단어단더] 여러 프로젝트를 하면서 ~ 와 같은 텍스트에서  [ ] 안에 있는 부분만 추출하고 싶습니다.

하지만, 그리디 방법에서는 [] 안에 것 이외에 다른 텍스트도 한꺼번에 가지고 올 수 있습니다.

[1. 단어 ] <- 닫힘은 무시하고 맨 뒤까지의 [3. 단어단더 ] <- 여기까지 멈춰서 1~3 사이의 텍스트 모두 긁어오는 게 그리디 방법입니다.

제어하기 위해서 ? 를 넣어줍니다.

text = ''' 
[1. 단어] 하는 이유는 ~ 
[2. 단어단어] 이라고 말하면서 ~ 
[3. 단어단더] 여러 프로젝트를 하면서 ~ 
'''

result = re.findall(r'\[.+?\]', text)
result #['[1. 단어]', '[2. 단어단어]', '[3. 단어단더]']

와 같이 greedy 검색을 피해 깔끔하게 열고 닫는 것을 지정할 수 있습니다.

 


여기 까지 정리했지만, 사실 이것보다 더 많은 옵션들.. 그리고 다른 방법들이 있습니다. 

어디에서 볼 수 있냐면 python 공식 문서 입니다. 

구글링의 끝은 공식문서와 함께


마무리

주로 python에서의 정규표현식을 정리했는데, vim도 거의 비슷합니다. 일부는 좀 다르게 짜야겠지만요! 정리하면서... 정규 표현식 컴파일 하지 않고 짠 코드들이 마음에 걸리네요(아직까진 잘 돌아갑디다...ㅋㅋㅋ) 원래 예정 일자 안에 포스팅을 올렸어야 했지만, 건강이 좋지 않아서 쉬었습니다. 환절기에 건강조심하시고, 정리한 글들이 도움이 되었으면 좋겠습니다. GoodLuck🤞


Reference

1. 정규 표현식 Group 과 관련한 이름 설정 및 참조와 관련하여 유용한 자료를 볼 수 있는 곳

https://wikidocs.net/4309#_6

 

08-3 강력한 정규 표현식의 세계로

이제 07-2에서 배우지 않은 몇몇 메타 문자의 의미를 살펴보고 그룹(Group)을 만드는 법, 전방 탐색 등 더욱 강력한 정규 표현식에 대해서 살펴보자. [TOC] ## 메…

wikidocs.net

2. 파이썬 정규 표현식 공식 document

https://docs.python.org/3/library/re.html#re.compile

 

re — Regular expression operations

Source code: Lib/re/ This module provides regular expression matching operations similar to those found in Perl. Both patterns and strings to be searched can be Unicode strings ( str) as well as 8-...

docs.python.org

3. 파이썬 정규표현식 참고자료

https://www.w3schools.com/python/python_regex.asp

 

Python RegEx

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

 

반응형