DL/NLP

[NLP]파이썬 re 모듈을 활용한 정규표현식

moonzoo 2024. 1. 12. 10:24

 

정규표현식?


정규표현식(Regular Expression 또는 Regex)은 문자열 패턴을 검색하고 조작하기 위해 쓰이는 형식 언어입니다.

주로 텍스트 처리 작업에서 사용되며, 특정 규칙에 맞는 문자열을 찾거나 변환하는 데에 유용합니다.

예를 들어, 이메일 주소, 전화번호, URL 등과 같은 특정 형식을 갖춘 문자열을 찾거나, 특정 단어나 패턴을 추출하거나 대체할 때 자주 사용됩니다.

메타 문자와 정규 문자

 


정규 표현식의 모든 문자는 메타 문자정규 문자 두가지로 분류됩니다.

 

메타 문자는 약속된 용법으로 사용되는 문자로, 특별한 의미를 가지고 있는 문자들을 의미합니다. 이러한 메타문자들은 특정 문자열 패턴을 찾거나 조작하는데 유용하게 쓰입니다.

 

정규 문자는 알파벳, 숫자 등 약속된 의미 없이 문자 그대로 사용되는 우리가 흔히 알고 있는 문자를 의미합니다.

 

메타 문자 표

(출처: https://www.ibm.com/docs/ko/rational-clearquest/8.0.1?topic=tags-meta-characters-in-regular-expressions)

\ 다음 문자를 특수 문자 또는 리터럴 중 하나로 표시합니다. 예를 들어, n은 문자 n과 일치하며 여기서 \n은 줄 바꾸기 문자와 일치합니다. 연속 \\는 \와 \와 일치합니다.
^ 입력의 시작과 일치합니다.
$ 입력의 종료와 일치합니다.
* 선행 문자와 0번 이상 일치합니다. 예를 들어 zo*는 z 또는 zoo와 일치합니다.
+ 선행 문자와 1번 이상 일치합니다.예를 들어 zo+는 zoo와 일치하지만 z와 일치하지 않습니다.
? 선행 문자와 0번 또는 1번 일치합니다. 예를 들어, a?ve? 는 never의 ve와 일치합니다.
. 줄 바꾸기 문자를 제외한 단일 문자와 일치합니다.
(패턴) 패턴을 일치시키고 일치를 기억합니다. 일치된 하위 문자열은 Item [0]...[n] 코드를 사용하여 결과적인 일치 콜렉션에서 검색할 수 있습니다. 소괄호 문자( )를 일치시키려면 \( 또는 \)를 사용하십시오.
x|y x 또는 y를 일치합니다. 예를 들어 z|wood는 z 또는 wood와 일치합니다. (z|w)oo는 zoo 또는 wood와 일치합니다.
{n} n은 비음수 정수입니다. 정확하게 n번 일치합니다. 예를 들어, o{2}는 Bob의 o와 일치하지 않지만 foooood의 첫 번째 두 개의 o와 일치합니다.
{n,} 이 표현식에서 n은 비음수 정수입니다. 최소한 n번 선행 문자와 일치합니다. 예를 들어 o{2,}는 Bob의 o와 일치하지 않지만 foooood의 모든 o와 일치합니다. o{1,} 표현식은 o+와 동등하며 o{0,}은 o*와 동등합니다.
{n,m} m  n 변수는 비음수 정수입니다. 최소한 n번 그리고 최대 m번 선행 문자와 일치합니다. 예를 들어 o{1,3}은 fooooood의 첫 번째 세 개 o와 일치합니다. o{0,1} 표현식은 o?와 동등합니다.
[xyz] 문자 세트. 괄호로 닫힌 모든 문자에 대응합니다. 예를 들어, [abc]는 plain의 a와 일치합니다.
[^xyz] 부정 문자 세트. 비괄호 문자와 일치합니다. 예를 들어, [^abc]는 plain의 p와 일치합니다 .
[a-z] 문자의 범위. 지정된 범위에 있는 모든 문자와 일치합니다. 예를 들어, [a-z]는 영어 알파벳에서 모든 소문자 알파벳 문자와 일치합니다. match
[^m-z] 문자의 부정 범위. 지정된 범위에 있지 않은 모든 문자와 일치합니다. 예를 들어, [m-z]는 m - z 범위 안에 있지 않은 모든 문자와 일치합니다.
\A 문자열의 시작에서만 일치합니다.
\b 단어 경계와 일치합니다. 즉 단어와 공백 사이의 위치입니다. 예를 들어, er\b는 matches the never의 er과 일치하지만 verb의 er과는 일치하지 않습니다.
\B 비단어 경계와 일치합니다. ea*r\B 표현식은 never early의 ear과 일치합니다.
\d 숫자 문자와 일치합니다.
\D 비숫자 문자와 일치합니다.
\f 용지 넘김 문자와 일치합니다.
\n 줄 바꾸기 문자와 일치합니다.
\r 캐리지 리턴 문자와 일치합니다.
\s 공백, 탭, 용지 넘김 문자 등을 포함하는 공백과 일치합니다.
\S 모든 비공백 문자와 일치합니다.
\t 탭 문자와 일치합니다.
\v 수직 탭 문자와 일치합니다.
\w 밑줄을 포함하는 모든 단어 문자와 일치합니다.이 표현식은 [A-Za-z0-9_]와 동등합니다.
\W 비단어 문자와 일치합니다.이 표현식은 [^A-Za-z0-9_]와 동등합니다.
\z 문자열의 끝에서만 일치합니다.
\Z 문자열의 끝에서 또는 끝에 있는 줄 바꾸기 문자 앞에서만 일치합니다.

 

파이썬 re 라이브러리


re는 "regular expression"의 약자로, 문자열에서 패턴을 검색하고 조작하는 데 사용됩니다.

re 라이브러리는 Python에서 정규표현식을 사용하기 위한 내장 라이브러리로 다양한 함수와 메소드를 제공합니다.

 

다음은 자주쓰이는 기능입니다.

1. re.compile(pattern, flags=0):

  • 정규표현식 패턴을 컴파일하여 객체를 반환합니다. 컴파일된 패턴은 나중에 여러 문자열에서 사용될 수 있습니다.
pattern = re.compile(r'\b\w+\b')

 

2. re.search(pattern, string, flags=0):

  • 문자열 전체에서 패턴과 일치하는 첫 번째 위치를 찾습니다. 일치하는 경우 match 객체를 반환하고, 없으면 None을 반환합니다.
result = re.search(r'\b\w+\b', 'Hello, World!')

 

3. re.match(pattern, string, flags=0):

  • 문자열의 시작부터 패턴과 일치하는지 확인합니다. search와 유사하지만 문자열의 시작에서만 일치를 찾습니다.
result = re.match(r'\b\w+\b', 'Hello, World!')

 

4. re.findall(pattern, string, flags=0):

  • 문자열에서 패턴과 일치하는 모든 부분을 찾아 리스트로 반환합니다.
result = re.findall(r'\b\w+\b', 'Hello, World! This is Python.')

 

5. re.finditer(pattern, string, flags=0):

  • findall과 유사하지만 일치하는 모든 부분을 반복 가능한 객체로 반환합니다.
result = re.finditer(r'\b\w+\b', 'Hello, World! This is Python.')

 

6. re.sub(pattern, repl, string, count=0, flags=0):

  • 문자열에서 일치하는 패턴을 다른 문자열로 대체합니다.
new_string = re.sub(r'\d+', 'NUM', 'The price is $100.')

 

7. re.split(pattern, string, maxsplit=0, flags=0):

  • 패턴을 기준으로 문자열을 나누어 리스트로 반환합니다.
parts = re.split(r'\s', 'This is a sentence.')

 

 

re 라이브러리 실습

해당 코드에서 re.compile() 함수를 사용해 정규표현식 패턴을 미리 컴파일하여 객체로 반환했습니다.

이렇게 컴파일된 패턴 객체를 사용하면 동일한 패턴을 반복해서 사용할 때 효율적입니다.

 

여기서 r'은 raw string을 나타내는 것으로, 정규표현식에서 백슬래시(\)를 이스케이프 문자로 인식하지 않게끔 하는 역할을 합니다. 

 

\b는 단어 경계를 의미하며, 

\w+: 하나 이상의 단어 문자 (알파벳, 숫자, 밑줄)를 의미합니다.

 

즉, 문장에 포함된 모든 단어를 모든 단어를 반환하게됩니다.

 

re.search와 re.match의 차이점

- re.match()는 문자열의 시작부터 검색을 시작합니다.

- re.search()는 문자열 전체를 검색하면서 첫 번째로 일치하는 부분을 찾습니다.

출력의 결과가 비슷한 경우가 많지만, 결과값에 차이가 있는 경우가 종종 등장합니다.

 

re.finditer, re.sub

 

re.finditer() 함수는 정규표현식과 일치하는 모든 부분을 반복 가능한 객체로 반환합니다. 각 일치에 대한 정보는 match 객체로 제공되며, 이를 통해 일치한 부분의 위치와 값을 가져올 수 있습니다. findall과 비슷한 역할을 합니다.

 

re.sub() 함수는 정규표현식과 일치하는 부분을 다른 문자열로 대체하는 데 사용됩니다. 이 함수는 원본 문자열을 변경하지 않고 새로운 문자열을 반환합니다.

 

마지막으로 re.split() 함수는 정규표현식에 맞게 문자열을 분할(split)하여 리스트로 반환합니다. 주어진 정규표현식 패턴을 기준으로 문자열을 나누어 각 부분을 리스트의 요소로 저장합니다.

텍스트 데이터를 활용한 실습


 

1. 데이터 불러오기

실습에 필요한 라이브러리와 데이터를 불러옵니다.

네이버의 영화 감정분석용 리뷰 데이터셋을 사용하도록 하겠습니다.

https://github.com/e9t/nsmc

 

2. 데이터 다뤄보기

Example 1. 리뷰 데이터 중 특수문자와 이모티콘으로 모델의 성능이 저하되고 있다. 이를 제거하라

 

이 코드에서는 re.sub(r'[^\w\s]', '', x)를 사용하여 'document' 컬럼의 각 텍스트에서 단어 문자(\w)와 공백 문자(\s)를 제외한 모든 문자를 제거합니다.

 

Example 2. 리뷰 데이터 중 노잼이라는 단어가 포함된 행을 출력하라.

 

크게 두가지 방법을 사용했습니다.

- DataFrame에서 특정 단어가 포함된 행들만 뽑아내기 위해서는 str.contains() 메서드를 사용할 수 있습니다.

 

- r'\b노잼\b'를 사용하여 정규표현식 패턴과 search 함수를 통해 각 텍스트에서 '노잼'이라는 단어를 찾습니다.

 

Example 3. 리뷰 데이터 중 노잼이라는 단어뒤에 공백이 오는 행과 오지 않는 행을 출력하라. (전방탐색)

전방탐색(lookahead)은 정규표현식에서 특정 패턴이 다음에 나오는지를 확인하는데 사용됩니다. 전방탐색은 실제로 일치하는 부분을 소비하지 않으며, 패턴이 일치하면 계속해서 나머지 부분을 검색합니다.

전방탐색은 다음 두 가지 종류로 나눌 수 있습니다.

  1. 긍정 전방탐색 ((?=...)): 주어진 패턴이 일치하는 경우에만 일치합니다.
  2. 부정 전방탐색 ((?!...)): 주어진 패턴이 일치하지 않는 경우에만 일치합니다.

Example 4. 리뷰 데이터 중 노잼이라는 단어뒤에 공백이 오는 행과 오지 않는 행을 출력하라. (후방탐색)

후방탐색(lookbehind)은 정규표현식에서 특정 패턴이 이전에 나왔는지를 확인하는데 사용됩니다. 후방탐색은 실제로 일치하는 부분을 소비하지 않으며, 패턴이 일치하면 이후의 부분을 계속해서 검색합니다.

후방탐색은 다음 두 가지 종류로 나눌 수 있습니다:

  1. 긍정 후방탐색 ((?<=...)): 주어진 패턴이 이전에 나왔는지 확인하며, 이전에 나왔다면 일치합니다.
  2. 부정 후방탐색 ((?<!...)): 주어진 패턴이 이전에 나오지 않았는지 확인하며, 이전에 나오지 않았다면 일치합니다.

마치며...

더 다양한 정규표현식을 연습하고자 한다면 아래의 사이트를 활용해도 좋을 것 같습니다. 

https://regexr.com/

 

RegExr: Learn, Build, & Test RegEx

RegExr is an online tool to learn, build, & test Regular Expressions (RegEx / RegExp).

regexr.com