[NLP] Python으로 Regular Expression 활용 예시

2024. 7. 7. 01:18Developers 공간 [Shorts]/Vision & Audio

728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew, 
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter

- Module Bundler  : React, Webpack, Parcel

B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS

C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight

 


1. What? (현상)

정규표현식(Regular Expression)은 프로그래밍 언어에서 문자열의 검색과 치환을 위해 사용하는 형식언어 입니다.

 

정규표현식은 " ^ + ? ( [ " 와 같이 원래 의미가 아닌 다른 뜻을 가진 Meta Characters들로 이루어져 있어 이들을 이해하고 사용하는 것이 중요합니다.

 

정규표현식을 이용한 변환은 웹페이지(https://regex101.com/r/rMbYHz/4)에서 확인할 수도 있고, 직접 코드에서 re 패키지 활용할 수도 있습니다. 

 

이에 대한 사용 예시를 여러가지 살펴보면서 이해해보려고 하는데, 이번 글에서는 문자 클래스 []를 활용하는 예시를 주로 보이겠습니다.


2. Why? (원인)

  • X

3. How? (해결책)

시작하기에 앞서 re 패키지의 기본적인 특징에 대해 설명하겠습니다.

 

아래와 같은 코드 중 Version1이 Version2과 다른 것은 compile을 활용해 미리 패턴을 지정해 놓은 것입니다.

import re 

# Version1
p = re.compile(r"[ㄱ-ㅣ가-힣]")
r = p.match(input) 
if not r is None:
	replaced = p.sub("", input)
    
# Version2
r = re.search(r"[ㄱ-ㅣ가-힣]", input)
if not r is None:
	replaced = re.sub(r"[ㄱ-ㅣ가-힣]", "", input)

 

이렇게 패턴을 검색할 때는 아래와 같이 세가지가 있습니다.

  • match() : 문자열의 처음부터 매치되는지 조사
  • search() : 문자열의 전체 중 일치되는 것이 있는지 조사
  • findall() : 문자열의 전체 중 일치되는 것이 있으면 리스트로 return
  • finditer() : 문자열의 전체 중 일치되는 것이 있으면 iter로 return  

 

이제 정규표현식을 표현하겠습니다. 정규표현식을 표현할 때 특징은 아래와 같습니다.

 

Pattern 설명 예제
r"" r을 붙여주는 경우는 backslash를 escape가 아닌 일반적인 문자로 인식하기 위해 사용합니다 re.compile("\n") : new line
re.compile(r"\n"):두개의 character
"^ $" 문장의 시작^과 문장의 끝$을 의미합니다. re.compile("^abc$")
\ 이스케이프(Escape)라 하며 해당 특수시퀀스를 알리기 위해 사용합니다.  \d : 숫자 [0-9]
\D : 숫자아님 [^0-9]
\w : 모든 문자+숫자 [a-zA-Z0-9_]
\W : 모든 문자+숫자 아닌것  [^a-zA-Z0-9_]
\s : white space [ \t\n\r\f\v]
[] 문자클래스로, 아래에서 설명하겠습니다.  
. dot 문자로, \n을 제외한 모든 문자를 의미합니다. re.compile("a.z") : abz, a0z... 등등
문자* 해당 문자가 0~무제한 반복 가능합니다. re.compile("ab*") : a, ab, abb ...
문자+ 해당 문자가 1~무제한 반복 가능합니다. re.compile("ab+"): ab, abb ...
{} 반복횟수를 제한할 때 사용합니다. re.compile("ab{2}"): abb
re.compile("ab{2,3}"): abb, abbb
? 0번~1번 반복하는 것으로, {0,1}과 같다. re.compile("ab?"): a, ab

 

 

그럼 이제 문자클래스를 살펴보겠습니다. 문자 클래스 []는 [ 와 ] 사이에 문자들을 표현해 일치하는지 확인할 수 있습니다.

Pattern 설명 예제
[문자들] 해당 문자들 중에 하나 [0-5][0-9] : 00에서 59까지의 두자리 숫자
[a-z] : 모든 소문자 ASCII문자
[0-9A-Fa-f] : 모든 16진수
[a-zA-Z] : 영어
[ㄱ-ㅣ가-힣] : 한글
[0-9] : 숫자
[^문자들] 해당 문자들이 아닌 문자들 중에 하나 [^5] : 5를 제외한 모든 문자

 

이정도로 살펴보고, 예시를 통해 살펴보겠습니다. 

 

Case0. 한글 찾아 없애기

 

앞에서 살펴봤었죠? 한글을 찾아 다 없애는 방법입니다.

 

한글을 normalize로 unicode로 확실히 만들어준 뒤 진행했으며, re.sub(pattern, target, text) 메서드를 활용하면 찾은 정규표현식의 문자열을 치환하기 위해 사용할 수 있습니다.

import re
from unicodedata import normalize

input = normalize('NFC', input.strip())
p = re.compile(r"[ㄱ-ㅣ가-힣]")
r= p.search(input)
if not r is None:
	replaced = p.sub("", input)

 

Case1. 시작할 때의 특정 패턴 찾아 고치기

 

시작할 때 패턴을 찾는 경우, 영어 또는 whitespace 가 아닌 부분을 없애주는 방법입니다.

p = re.compile("한글 [0-9][0-9] ")
p1 = re.compile("한글 [0-9][0-9]M [0-9][0-9] ")

r = p.match(input)
r1= p1.match(input)

 if not r is None or not r1 is None:
    replaced = re.sub(r"[^a-zA-Z\s]","", input).strip()
p = re.compile(r"^[ABC][0-9][0-9][0-9] ")

r = p.match(input)

if not r is None:
    value1 = input.split(" ")[0].strip()
    value2 = p.sub("", input).strip()

 

Case3. 시작과 끝의 패턴 찾아 고치기1

 

시작할 때 뿐 아니라 끝날때의 패턴도 찾아 고치는 방법입니다. 조금 자세히 보겠습니다.

  • ^ : string의 시작
  • [0-9]+ : 1개이상의 숫자가 연속되고
  • _ : 언더바가 존재하며
  • [a-zA-Z ]* : white space 혹은 영어가 존재하는 string을 지난후에
  • _ : 언더바가 존재하며
  • [0-9]+ : 1개 이상의 숫자가 연속되고
  • $ : string의 끝
p = re.compile(r"^[0-9]+_[a-zA-Z ]*_[0-9]+$")

r = p.match(input)

if not r is None : 
    replaced = input.split("_")[1].strip()

 

 

Case4. 시작과 끝의 패턴 찾아 고치기2

 

다른 경우를 살펴보겠습니다. 가운데 "언더바를 제외한 문자"를 명시해서 언더바를 이용해 구분할 수 있게 해주었습니다.

  • ^ : string의 시작
  • [0-9]+ : 1개이상의 숫자가 연속되고
  • _ : 언더바가 존재하며
  • [^_]* : 언더바를 제외한 어떤 문자든 반복
  • _ : 언더바가 존재하며
  • [0-9]+ : 1개 이상의 숫자가 연속되고
  • _ : 언더바가 존재하며
  • ABC: ABC라는 문자가 존재하며
  • $ : string의 끝
p = re.compile(r"^[0-9]+_[^_]*_[0-9]+_ABC$")

r = p.match(input)

if not r is None : 
    replaced = input.split("_")[1].strip()

 

 

Case5. 시작과 끝의 패턴 찾아 고치기3

계속 해서 다른 패턴을 보겠습니다.아래는 . 과 white space가 섞여있습니다.

  • ^ : string의 시작
  • [0-9]+ : 1개이상의 숫자가 연속되고
  • \. : dot 이 존재하며
  • [a-zA-Z ]* : white space 혹은 영어가 존재하는 string을 지난후에
  •  : white space 가 존재하며
  • [0-9_]+ : 1개 이상의 숫자 혹은 언더바가 연속되고
  • $ : string의 끝
p = re.compile(r"^[0-9]+\.[a-zA-Z ]* [0-9_]+$")

r = p.match(input)

if not r is None : 
    replaced = " ".join(input.split(".")[1].split("_")[0].split(" ")[:-1]).strip()

 

 

Case6.시작과 끝의 패턴 찾아 고치기4

 

마지막으로 복습겸 다시 보겠습니다.

  • ^ : string의 시작
  • [0-9][0-9] : 2개의 숫자
  • _ : 언더바가 존재하며
  • [^_]* : 언더바가 아닌 어떤 문자든 반복
  • _ : 언더바가 존재하며
  • [0-9_\,]+ : 1개 이상의 숫자 혹은 언더바 혹은 콤마가 연속되고
  • ABC : ABC라는 문자가 존재하고
  • $ : string의 끝
p = re.compile(r"^[0-9][0-9]_[^_]*_[0-9_\,]* ABC$")

r = p.match(input)

if not r is None : 
    replaced = input.split("_")[1].strip()

 


 

 

728x90
반응형