본문 바로가기
NLP/기초

정규표현 유용한 표현 모음집

by 나는 라미 2024. 6. 18.
728x90
반응형

자연어처리를 이용해 데이터를 다루다 보면 정규표현을 정말 많이 사용하게 된다.

개념만 알고 사용하다가 알게된 여러 케이스들을 정리해두면 좋을 것 같아서 기록해둔다.

 

괄호 표현법

괄호가 의미하는 바를 잘 알아야한다. 각 괄호마다 사용하는 방법이 다르기 때문에 이를 잘 이해하고 사용하면 좀 더 유용하게 정규표현을 사용할 수 있다.

 

1. 소괄호 ()

 

1) 그룹화

패턴을 그룹으로 묶어 하나의 단위로 처리할 때 사용한다. 예를 들어 "(apple)"이라고 사용하면 apple을 하나의 단위로 검색하게 된다. "(appple|banana)"라고 사용하면 "apple"이나 "banana"를 하나의 단위로 검색한다.

 

2) 캡처링

그룹화 하여 매칭된 단어를 저장하는 기능이다. 캡처된 그룹은 결과에서 추출하거나 명명하여 재사용 할 수 있다.

import re

text = "abc 123 abc 456"
pattern = r"(\w+) (\d+)\s+\1 (\d+)"  # 첫 번째 캡처 그룹을 재사용

match = re.search(pattern, text)
if match:
    print(match.group(0))  # 전체 매칭된 부분
    print(match.group(1))  # 첫 번째 캡처 그룹
    print(match.group(2))  # 두 번째 캡처 그룹
    print(match.group(3))  # 세 번째 캡처 그룹
abc 123 abc 456
abc
123
456

 

패턴에서 첫번째 그룹을 '\1'을 이용하여 재사용한 것을 알 수 있다. 

 

2. 중괄호 {}

 

중괄호는 수량자를 정의해 패턴이 나타나는 횟수를 지정하는 역할을 한다. 

예를 들어 "a{2,3}" 이라면 'a'가 2번이상 3번 이하로 나타난 경우를 추출하게 된다.

 

3. 대괄호 []

 

대괄호는 문자클래스를 정의해 대괄호 안에 포함된 문자들 중 하나와 매칭하도록 한다.

import re

text = "cat bat mat rat"
pattern = "[cbm]at"

result = re.findall(pattern, text)
['cat', 'bat', 'mat']

문자 클래스 안에 포함된 'c' or 'b' or 'm'으로 시작되며 'at'가 오는 문자열을 선택하기 위한 패턴이다. 단어가 붙어있으면 or의 의미를 가지게 되며 대괄호 안에 있는 '|'는 더이상 or연산자가 아닌 문자 '|'로 인식된다. '('역시 그룹화/캡쳐링의 기능이 아닌 기호 '('로 인식된다. 

 

모든 문자 표현

추출하고자 하는 것에 따라 표현이 달라지겠지만, 추출하고 싶은 위치가 정해져 있고 그 사이의 모든 문자를 추출할 때 유용하게 쓰인다.

여러 가지로 표현 할 수 있지만 나는 주로 2개로 표현한다.

 

1. .*

2. [\W|\w]

 

(.*) 의 의미는 줄바꿈을 제외한 모든 문자가 0번이상 반복되는 것을 의미한다. 대부분의 경우 (.*) 을 쓸때 더 직관적이고 유용하게 쓰일 때가 많다.

하지만 줄바꿈이 포함된 경우도 처리해야할 일이 생길 수 있다. 예를 들어 기사의 일부 내용을 추출할때 줄바꿈이 포함해야 원하는 단락을 추출 할 수 있기 때문이다.

 

그럴때 사용하는 패턴이 [\W|\w]이다. [\W|\w]은 \w(단어, 알파벳 + 숫자 + _ 중의 한 문자 )와 \W(단어가 아닌 문자)를 0번 이상 반복되는 것을 의미한다. 그렇기 때문에 (.*)이 포함하지 못한 줄바꿈을 포함할 수 있다. 

 

문자 클래스를 나타내는 대괄호[] 안에서 or연산자(|)는 작동하지 않지만 가시적으로 구분하기 위해서 작성했다.

\W를 사용했기 때문에 문자'|'는 이미 여기에 포함되어 의미있게 작동하지 않는다.

 

import re

text = "날짜:2024-01-01\n 일기:\n내일 뮤지컬을 보러갈거야.\n 너무 신난다!!\n 뮤지컬을 보고 난 후 뭐먹지?\n"
diary1 = re.findall("일기:\n([\w|\W]*)", text)
diary2 = re.findall("일기:\n(.*)", text)

print(diary1)
print(diary2)
['내일 뮤지컬을 보러갈거야.\n 너무 신난다!!\n 뮤지컬을 보고 난 후 뭐먹지?\n']  #diary1
['내일 뮤지컬을 보러갈거야.']  #diary2

예시 문서에서 일기에 해당하는 부분을 추출하려고 한다.

(.*) 패턴은 줄바꿈이 나오기 전까지 추출하였지만

[\W|\w] 은 일기에 해당하는 부분을 모두 추출한 결과를 볼 수 있다.

 

탐욕 수량자 Greedy / Lazy Non Greedy

 

정규표현의 메타문자는 기본적으로 greedy하게 작동한다.

예를 들어 "오늘 점심 메뉴는 갈비탕이야. 내일 점심 메뉴는 설렁탕이야." 문장에서 메뉴를 추출하고자 한다. 

import re

text = "오늘 점심 메뉴는 갈비탕이야. 내일 점심 메뉴는 설렁탕이야."
menu1 = re.findall("메뉴는 ([\w|\W]*?)이야", text)
menu2 = re.findall("메뉴는 ([\w|\W]*)이야", text)
print(menu1)
print(menu2)
['갈비탕', '설렁탕']	#menu1
['갈비탕이야. 내일 점심 메뉴는 설렁탕']  #menu2

 

메타문자 *은 greedy 하게 움직이기 때문에 최대한 많은 문자를 포함하려고 한다. "메뉴는 " - "이야" 사이의 단어를 추출하고자 할때, menu2처럼 [\W|\w] *을 사용하게 되면 최대한 많은 단어를 포함 하기 위해 뒤에 있는 "이야"까지의 단어를 추출한다. 

먼저 나오는 "이야"에서 끊고 싶을때 lazy한 방법을 선택한다. 가장 먼저 나온 일치 패턴이 나오면 바로 추출하는 방법이다. lazy하게 이용하고 싶으면 '?'를 이용한다. menu1의 예시 처럼 먼저 나온 "이야"앞에서 잘 끊어서 추출한 결과를 볼 수 있다.

 

아는데 기억이 안나서 헷갈리는 것 중에 최고였다...

정리하면서 완벽하게 습득하는 중..

생각보다 꽤 많이 활용하는 것 중에 하나인듯..!

번외로 '?'의 활용은 경우에 따라 3가지로 나뉜다.

1. 전방 탐색 / 후방 탐색 등 탐색을 뜻할때 사용

2. 문자가 0개 또는 1개 있을때 확인하는 용도로 사용 ex. x?

3. lazy하게 만들기 위해 사용

 

or(|) 연산자 똑똑하게 활용하기

여러 값 중 하나라도 포함되어 있을 때 사용하는 or연산자를 사용할 때의 꿀팁이다.

정규 표현식의 엔진은 문자열을 왼쪽에서 오른쪽으로 읽으며, 가능한 첫 번째로 매칭되는 패턴을 찾는다.

매칭되는 패턴을 찾으면 그 패턴을 이용하여 나머지 부분을 계속해서 평가하게 된다.

import re

text = "This text can consist of multiple patterns, including consist."
or1 = re.findall("consist|consist of", text)
or2 = re.findall("consist of|consist", text)

print(or1)
print(or2)
['consist', 'consist']	#or1
['consist of', 'consist']	#or2

 

or1처럼 더 짧은 패턴을 앞에 위치하게 되면 원하는 답을 못찾을 가능성이 있다.

or연산자를 이용해서 여러 패턴을 나열할 때 긴 패턴을 먼저 위치하도록 해야한다.

 

 

 

 

728x90
반응형

'NLP > 기초' 카테고리의 다른 글

[#1 실습] 정규표현식 함수 사용법  (3) 2021.12.01
[#1 이론] 정규표현식  (1) 2021.12.01
[#0] 시작  (3) 2021.12.01

댓글