language/Python

[Python] re 모듈, 정규 표현식(정규식) 개념과 완전 정복하기(regex cheat sheet) SQL, HIVE, PySpark에서의 regex

스파이디웹 2022. 4. 9. 10:50
728x90

이번 포스트에서는 정규표현식의 개념과, python에서 정규표현식을 지원하는 re 모듈에 대해서 정리하겠습니다.


1. 정규 표현식이란?

정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용, 정규식이라고도 부름

 


2. 정규 표현식의 기초, 메타 문자

※ 메타 문자란 원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자
. ^ $ * + ? { } [ ] \ | ( )

1) 문자 클래스 [ ]

  • 문자 클래스로 만들어진 정규식은 '[ ] 사이의 문자들과 매치'를 의미

예를 들어, 정규 표현식

[abc]

는 'a, b, c 중 한 개의 문자와 매치'를 뜻함

 

즉, 어떠한 문자열에("defg") 위의 정규식이 하나도 해당되지 않을 때, '[abc]정규식와 매치하지 않는다.' 라고함


  • [ ] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위(From - To)를 의미

예를 들어, 정규 표현식

[a-z]

는 [abcdefghijklmnopqrstuvwxyz]와 같음.


  • 문자 클래스를 사용한 대표적으로 많이 쓰이는 정규식은 다음과 같음
[a-zA-Z]#모든 영문자
[0-9]#모든 숫자
[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]#모든 한글

자주 사용하는 문자 클래스

  • \d - 숫자와 매치, [0-9]와 동일한 표현식
  • \D - 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식
  • \s - whitespace 문자와 매치, [ \t\n\r\f\v]와 동일한 표현식, 맨 앞의 빈 칸은 공백문자(space)를 의미
  • \S - whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식
  • \w - 문자+숫자(alphanumeric)와 매치, [a-zA-Z0-9_]와 동일한 표현식
  • \W - 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식

대문자로 사용된 것은 소문자의 반대임을 추측할 수 있다.


2) Dot(.)

  • 정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨을 의미
a.b

는 'a + 모든문자 + b'를 뜻함

a와 b라는 문자 사이에 어떤 문자가 들어가도 모두 매치된다는 의미

*a와 b사이에 어떠한 문자가 반드시 하나는 있어야 된다.


  • 메타 문자가아닌 점(dot)을 의미하는 문자 '.' 를 사용하려면
a[.]b

'a + Dot(.)문자 + b'를 뜻함


3) 반복 (*)

  • 반복을 의미하는 *
ca*t
정규식 문자열 Match 여부 설명
ca*t ct Yes "a"가 0번 반복되어 매치
ca*t cat Yes "a"가 0번 이상 반복되어 매치 (1번 반복)
ca*t caaat Yes "a"가 0번 이상 반복되어 매치 (3번 반복)

 


4) 반복 (+)

  • 반복을 나타내는 또 다른 메타 문자 +(최소 1번 이상 반복)
ca+t

'c + a(1번 이상 반복) + t'를 뜻함

 

정규식 문자열 Match 여부 설명
ca+t ct No "a"가 0번 반복되어 매치되지 않음
ca+t cat Yes "a"가 1번 이상 반복되어 매치 (1번 반복)
ca+t caaat Yes "a"가 1번 이상 반복되어 매치 (3번 반복)

 


5) 반복 ({m,n}, ?)

  • 반복 횟수를 지정하는 메타 문자
ca{2}t

'c + a(반드시 2번 반복) + t'

정규식 문자열 Match 설명
ca{2}t cat No "a"가 1번만 반복되어 매치되지 않음
ca{2}t caat Yes "a"가 2번 반복되어 매치
 {3,}#사용하면 반복 횟수가 3 이상
 {,3}#사용하면 반복 횟수가 3 이하 의미 #생략된 m은 0과 동일)
{1,}은 +와 동일하고, {0,}은 *와 동일

ca{2,5}t

'c + a(2~5회 반복) + t'

정규식 문자열 Match 여부 설명
ca{2,5}t cat No "a"가 1번만 반복되어 매치되지 않음
ca{2,5}t caat Yes "a"가 2번 반복되어 매치
ca{2,5}t caaaaat Yes "a"가 5번 반복되어 매치

  • {0, 1}을 의미하는 ? 메타 문자
ab?c

'a + b(있어도 되고 없어도 된다) + c'

정규식 문자열 Match 여부 설명
ab?c abc Yes "b"가 1번 사용되어 매치
ab?c ac Yes "b"가 0번 사용되어 매치

 b 문자가 있거나 없거나 둘 다 매치되는 경우


2. 파이썬에서 정규 표현식을 지원하는 re 모듈

파이썬은 정규 표현식을 지원하기 위해 re(regular expression의 약어) 모듈을 제공

re module은 파이썬 설치시 기본적으로 탑재되어 있는 기본 라이브러리

import re
p = re.compile('ab*')

1) 정규식을 이용한 문자열 검색

method 목적
match() 문자열의 처음부터 정규식과 매치되는지 조사한다.
search() 문자열 전체를 검색하여 정규식과 매치되는지 조사한다.
findall() 정규식과 매치되는 모든 문자열(substring)을 리스트로 돌려준다.
finditer() 정규식과 매치되는 모든 문자열(substring)을 반복 가능한 객체로 돌려준다.

match, search는 정규식과 매치될 때는 match 객체를 돌려주고, 매치되지 않을 때는 None을 돌려줌


match

import re
text = "abc"
text2 = "3 python"

p = re.compile('[a-z]+')
m = p.match(text)
n = p.match(text2)
print(m)
#<re.Match object; span=(0, 3), match='abc'> 출력
print(n)
#None 출력
if m:
    print('Match found: ', m.group())
else:
    print('No match')
#Match found: abc 출력
if n:
    print('Match found: ', m.group())
else:
    print('No match')
#No match 출력

match 객체의 메서드

  • match 객체의 어떤 문자열이 매치되었는지
  • 매치된 문자열의 인덱스 시작과 끝
method 목적
group() 매치된 문자열을 돌려준다.
start() 매치된 문자열의 시작 위치를 돌려준다.
end() 매치된 문자열의 끝 위치를 돌려준다.
span() 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다.
#Match 객체의 메소드
print(m.group(),m.start(),m.end(),m.span())
#abc, 0, 3, (0,3) 출력

search

import re
text = "abc"
text2 = "3 python"

p = re.compile('[a-z]+')
a = p.search(text)
print(a)
#<re.Match object; span=(0, 3), match='abc'> 출력
b = p.search(text2)
print(b)
#<re.Match object; span=(2, 8), match='python'> 출력

findall

import re

text3 = "life is too short"
q = p.findall(text3)
print(q)
#['life', 'is', 'too', 'short'] 출력

finditer

import re

text3 = "life is too short"

w = p.finditer(text3)
print(w)
#<callable_iterator object at 0x000001A3A8388400> 출력
for i in w:
    print(i)
#<re.Match object; span=(0, 4), match='life'>
#<re.Match object; span=(5, 7), match='is'>
#<re.Match object; span=(8, 11), match='too'>
#<re.Match object; span=(12, 17), match='short'>

컴파일 옵션

  • DOTALL(S): . 이 줄바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 함
  • IGNORECASE(I): 대소문자에 관계없이 매치할 수 있도록 함
  • MULTILINE(M): 여러줄과 매치할 수 있도록 함 (^, $ 메타문자의 사용과 관계가 있는 옵션)
  • VERBOSE(X): verbose 모드를 사용할 수 있도록 함 (정규식을 보기 편하게 만들수 있고 주석등을 사용할 수 있게됨)

옵션을 사용할 때는 re.DOTALL처럼 전체 옵션 이름을 써도 되고 re.S처럼 약어를 써도 됨

 


white space, 백슬래쉬(\) 문제와 Raw String 규칙

백슬래쉬로 인해 white space가 문자를 표현 할 수 있는데, 정규식에서 \자체를 표현하려면 2번을 사용해서 표현

#6. 백슬래쉬와 Raw String 규칙
#\t\n\r\f\v 와 같이 white space에 대해서는 정규식을 적용하고 싶다면 \를 2번 사용
p = re.compile('\\nowhitespace')
print(p.search('example\nowhitespace'))
#<re.Match object; span=(7, 19), match='\nowhitespace'> 출력

p = re.compile('\\\\nowhitespace')
print(p.search('example\\nowhitespace'))
#<re.Match object; span=(7, 20), match='\\nowhitespace'> 출력

백슬래쉬를 1번만 사용하면서 \문자를 있는 그대로 표현하고 싶다면 Raw String 규칙인 r문자를 사용

p = re.compile(r'\nowhitespace')
print(p.search('example\nowhitespace'))
#<re.Match object; span=(7, 19), match='\nowhitespace'> 출력

3. 메타문자(문자열을 소비시키지 않는 메타 문자)

+, *, [], {} 등의 메타문자는 매치가 진행될 때 현재 매치되고 있는 문자열의 위치가 변경된다(보통 소비된다고 표현


|

  • 메타 문자는 or과 동일한 의미
>>> p = re.compile('Crow|Servo')
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0, 4), match='Crow'>

^

  • ^ 메타 문자는 문자열의 맨 처음과 일치함을 의미
  • re.MULTILINE을 사용할 경우에는 여러 줄의 문자열일 때 각 줄의 처음과 일치
>>> print(re.search('^Life', 'Life is too short'))
<re.Match object; span=(0, 4), match='Life'>
>>> print(re.search('^Life', 'My Life'))
None

$

  • $ 메타 문자는 ^ 메타 문자와 반대의 경우
  • 문자열의 끝과 매치함을 의미
>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12, 17), match='short'>
>>> print(re.search('short$', 'Life is too short, you need python'))
None

\A

  • \A는 문자열의 처음과 매치됨을 의미
  • ^ 메타 문자와 동일한 의미이지만 re.MULTILINE 옵션을 사용할 경우에는 다르게 해석
  • re.MULTILINE 옵션을 사용할 경우 ^은 각 줄의 문자열의 처음과 매치되지만 \A는 줄과 상관없이 전체 문자열의 처음하고만 매치

\Z

  • \Z는 문자열의 끝과 매치됨을 의미
  • \A와 동일하게 re.MULTILINE 옵션을 사용할 경우 $ 메타 문자와는 달리 전체 문자열의 끝과 매치

\b

  • \b는 단어 구분자(Word boundary)
  • \b는 파이썬 리터럴 규칙에 의하면 백스페이스(BackSpace)를 의미하므로 백스페이스가 아닌 단어 구분자임을 알려 주기 위해 r'\bclass\b'처럼 Raw string임을 알려주는 기호 r을 반드시 붙여주어야 함
>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))  
<re.Match object; span=(3, 8), match='class'>

\B

  • \B 메타 문자는 \b 메타 문자와 반대의 경우
  • whitespace로 구분된 단어가 아닌 경우에만 매치

4. 그룹핑

문자열이 계속해서 반복되는지 조사하는 정규식을 그룹핑을 통해 해결

(ABC)+

위와 같이 문자를 괄호로 묶음으로써 그룹핑 시킴

import re

p = re.compile(r"\w+\s+\d+[-]\d+[-]\d+")#이름 + " " + 전화번호 형태의 문자열을 찾는 정규식
m = p.search("park 010-1234-1234")

#이름만 뽑아내는 정규식
p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")#0번 째 그룹은 park 010-1234-1234, 1번 째 그룹은 park이다.
m = p.search("park 010-1234-1234")
print(m.group(1))#park 출력

#이름과 전화번호를 그룹핑하는 정규식
p = re.compile(r"(\w+)\s+(\d+[-]\d+[-]\d+)")
m = p.search("park 010-1234-1234")
print(m.group(1),m.group(2))#park 010-1234-1234 출력

#이름과 전화번호와 국번만 뽑아내는 정규식
p = re.compile(r"(\w+)\s+((\d+)[-]\d+[-]\d+)")#2번 째 그룹 안에 중첩된 그룹을 만들 수 있다(3번 째 그룹)
m = p.search("park 010-1234-1234")
print(m.group(1), m.group(2), m.group(3))#park 010-1234-1234 010 출력

 

group(인덱스) 설명
group(0) 매치된 전체 문자열
group(1) 첫 번째 그룹에 해당되는 문자열
group(2) 두 번째 그룹에 해당되는 문자열
group(n) n 번째 그룹에 해당되는 문자열

그룹핑 문자열 재참조

#그룹핑 문자열 재참조
p = re.compile(r'(\b\w+)\s+\1')#(\b\w+)\s+\1은 (그룹) + " " + 그룹과 동일한 단어, 2개의 동일한 단어를 연속적으로 사용해야만 매치
m = p.search('hello python python python bye')
print(m.group())#python python 출력

그루핑된 문자열에 이름 붙이기

#그룹핑 문자열 이름 붙여 재참조
p = re.compile(r'(?P<myfirstgroup>\b\w+)\s+(?P=myfirstgroup)')#그룹핑 명에 이름을 부여하면 (?P=그룹핑 명) 처럼 사용하여 재참조한다.
m = p.search("hello python python python bye")
print(m.group())#python python 출력

전방형 탐색

예를 들어, http:라는 검색 결과에서 :을 제외하고 출력하고자 할 때 사용

  • 긍정형 전방 탐색((?=...)) - ... 에 해당되는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않음
  • 부정형 전방 탐색((?!...)) - ...에 해당되는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소비되지 않음
.*[.](?!bat$).*$ #확장자가 bat가 아닌 경우에만 통과된다는 의미
.*[.](?!bat$|exe$).*$ #확장자가 bat이거나 exe이면 제외하라는 조건

문자열 바꾸기

sub 메소드를 이용하여 문자열을 바꿀 수 있음

#문자열 변환 sub 메소드
p = re.compile(r'(score|password|grade)')
m = p.sub('encoding target','your score, password, grade, name, telephone is here')#1번째 매개변수자리엔 변환할 문자열, 2번째 문자열엔 적용 대상 문자열
print(m)#your encoding target, encoding target, encoding target, name, telephone is here 출력

m = p.sub('encoding target','your score, password, grade, name, telephone is here',count=1)#맨 앞의 1개만 변환
print(m)#your encoding target, password, grade, name, telephone is here 출력

m = p.subn('encoding target','your score, password, grade, name, telephone is here',2)#맨 앞의 2개만 변환하여 튜플로 출력
print(m)#('your encoding target, encoding target, grade, name, telephone is here', 2) 출력

 


SQL, HIVE에서의 regex

ORACLE

SQL Description
REGEXP_LIKE(열 이름, 조건) 정규 표현식을 사용하여 조건 검색을 실행
REGEXP_REPLACE(문자열 또는 열 이름, 조건, 치환 문자열) 지정한 정규 표현식에 일치하는 부분을 지정한 다른 문자열로 치환함
REGEXP_INSTR(문자열 또는 열 이름, 조건) 지정한 조건(정규 표현식)에 일치하는 부분의 최초의 위치를 반환함.
REGEXP_SUBSTR(문자열 또는 열 이름, 조건) 지정된 정규 표현식에 일치하는 부분 문자열을 찾아 결과로 리턴

MySQL

version 8이전 version8이상 설명
REGEXP REGEXP WHERE 문에 사용하며, 기본적으로 정규식을 사용할 때 사용하는 함수
RLIKE REGEXP_LIKE WHERE 문에 사용하며, REGEXP랑 같은 용도로 사용된다. 정규식을 사용할 때 사용하는 함수
사용불가 REGEXP_REPLACE SELECT 문에 사용하며,
SELECT REGEXP_REPLACE(대상 컬럼, 대상 문자열, 변경 문자열)
과 같이 사용한다.

REGEXP

address 컬럼이 특수기호가 들어간 row만 출력

RLIKE

address 컬럼이 특수기호가 들어간 row만 출력

 

REGEXP_LIKE

address 컬럼이 특수기호가 들어간 row만 출력

REGEXP_REPLACE

actor 테이블의 last_update 컬럼이 숫자가 있으면 *로 변환


HIVE

hive에서의 정규식도 mysql과 사용하는 함수가 대부분이 같다.'

기본적으로 2개의 함수는 대표적으로 많이 쓰인다.

RLIKE

REGEXP_REPLACE

IF, RLIKE, REGEXP_REPLACE를 활용하여 다음과 같이 address컬럼에 특수기호가 들어가 있는 row에 숫자를 마스킹 처리 하도록 표현할 수도 있다.

다만 하이브는 특수기호 자체를 표현하는데에 있어서 \사용에 유의해야 한다.

REGEXP_REPLACE (CL, '\(\\d+\)\.\(\\d+\)\.\(\\d+\)', '')

위의 하이브 수식은 아래의 파이썬에서 의미하는 정규식의 의미를 나타낸다.

(정확히는 파이썬이 갖고 있는 함수가아닌 pyspark에서의 REGEXP_REPLACE)

REGEXP_REPLACE(CL,'(\d+)\.(\d+)\.(\d+)','')

PySpark에서의 regex

 

PySpark의 정규식은 특수기호 자체를 표현하는데 있어서, HIVE혹은 MYSQL과는 다르게 python언어를 따라가게 된다.

기본적으로 2개의 함수는 대표적으로 많이 쓰인다.

RLIKE

REGEXP_REPLACE

F.when(F.col('a.UI_VER').rlike('^[0-9]*$'),F.regexp_replace(F.col('a.CL'),'(\d+)\.(\d+)','')).otherwise(F.lit(0))

 

F.regexp_replace(F.col('a.CL'),'(\d+)\.(\d+)\.(\d+)','')

참조:

https://wikidocs.net/4308

https://itbellstone.tistory.com/88

728x90