정리 노트

re 모듈과 같이 정규식 이해하기(Python) 본문

개념 정리

re 모듈과 같이 정규식 이해하기(Python)

꿈만 꾸는 학부생 2022. 6. 23. 22:02
728x90

이 글은 아래의 사이트를 보면서 정리해보는 글이다.

https://docs.python.org/ko/3.8/howto/regex.html?highlight=%EC%A0%95%EA%B7%9C%EC%8B%9D 

 

정규식 HOWTO — Python 3.8.13 문서

소개 정규식(RE, regexes 또는 regex 패턴이라고 불립니다)은 본질적으로 파이썬에 내장된 매우 작고 고도로 특수화된 프로그래밍 언어이며, re 모듈을 통해 사용할 수 있습니다. 이 작은 언어를 사용

docs.python.org

메타 문자

메타 문자를 통해 반복 등을 표시할 수 있다. 메타 문자의 종류들은 아래와 같다. 각 메타 문자들이 어떤 기능을 하는지 알아보자.

. ^ $ * + ? { } [ ] \ | ( )

[ ] : 일치시키려는 문자 집합인 문자 클래스를 지정하는 데 사용. 문자는 개별로 나열되거나, '-'를 통해 범위를 나타낼 수 있다.

      안에 들어가는 메타 문자는 메타 문자의 기능이 없어지고 문자로만 인식된다.

ex) [abc] -> a 또는 b 또는 c와 일치, [a-c]와 같다.    [akm$] -> a 또는 k 또는 m 또는 '$'와 일치.

 

^ : 클래스에 나열되지 않은 문자를 일치시키는데 사용. 클래스의 첫 번째 문자로 '^'을 포함시킨다.

     첫 번째가 아닌 다른 곳에 위치하면 메타 문자의 기능이 없어지고 문자로만 인식된다.

ex) [^5] -> '5'를 제외한 모든 문자와 일치한다.    [5^] -> '5' 또는 '^'와 일치

 

\ : 메타 문자들을 이스케이프 처리해 문자로 인식하게 한다.    ex) \\ -> '\'와 일치한다.    \[ -> '['와 일치한다.

    \로 시작하는 특수 시퀀스 중 일부는 숫자 집합, 글자 집합 등 미리 정의된 문자 집합을 나타낸다.

    시퀀스를 클래스 안에 포함시킬 수 있다.    ex) [\s,.] -> 모든 공백 문자 또는 ',' 또는 '.'와 일치한다.

  • \d : 모든 10진수 숫자와 일치, [0-9]와 동일
  • \D : 숫자가 아닌 모든 문자와 일치, [^0-9]와 동일
  • \s : 모든 공백 문자와 일치, [ \t\n\r\f\v]와 동일
  • \S : 공백이 아닌 모든 문자와 일치, [^ \t\n\r\f\v]와 동일
  • \w : 모든 영어와 10진수 숫자와 일치, [a-zA-Z0-9_]와 일치
  • \W : 영어와 10진수 숫자가 아닌 모든 문자와 일치, [^a-zA-Z0-9_]와 일치
  • \A : MULTILINE 모드가 아니면 ^과 동일
  • \Z : 문자열 끝부분에서만 일치
  • \b : 단어의 경계를 표시    ex) \bclass\b -> class와 일치하지만 classification과 일치하지 않는다.

* : 이전 문자가 0번 이상 반복하는 문자를 일치시킨다.    ex) ca*t -> ct, cat, caat, caaat,... 와 일치한다.

+ : 이전 문자가 1번 이상 반복하는 문자를 일치시킨다.   ex) ca+t -> cat, caat, caaat, ... 와 일치한다.

? : 이전 문자가 0번 또는 1번 나오는 문자를 일치시킨다.    ex) home-?brew -> homebrew, home-brew와 일치한다.

{a, b} : 이전 문자가 최소 a번, 최대 b번 나오는 문자를 일치시킨다. a와 b는 십진수다.  ex) a/{1, 3}b -> a/b, a//b, a///b와 일치한다.

            a와 b는 생략이 가능하다. a가 생략되면 0으로 해석되고, b가 생략되면 무한대로 해석한다.          

            {}를 이용해 *, +, ?를 표현할 수 있다.

 

| : or 연산자라 생각하면 된다.    ex) Crow|Servo -> Crow 또는 Servo와 일치한다.

$ : 줄의 끝부분과 일치한다. 문자열의 끝이나 줄 바꿈 문자 다음에 오는 모든 위치로 정의된다.   ex) }$ -> }와 일치한다.

() : 괄호 내부의 표현식을 묶고 반복 한정자 등으로 그룹의 내용이 반복되는 문자와 일치시킬 수 있다.

      ex) (ab)* -> ab, abab, ababab,... 와 일치한다.

re 모듈과 같이 정규식 사용해보기

re 모듈은 정규식을 객체로 컴파일한 다음 작업을 수행할 수 있도록 한다. 아래와 같이 컴파일을 수행할 수 있다.

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

일치하는 패턴 찾기

일치하는 패턴을 찾는 여러 method들이 있다. 대표적인 네 가지는 아래와 같다.

method purpose
match() 문자열의 시작 부분에서 일치하는지 판단한다. 일치하는 항목이 없으면 None을 반환한다.
search() 패턴이 일치하는 위치를 찾으면서 문자열을 훑는다. 일치하는 항목이 없으면 None을 반환한다.
findall() 패턴이 일치하는 모든 부분 문자열을 리스트로 반환한다.
finditer() 패턴이 일치하는 모든 문자열을 iterator로 반환한다.

예시:

>>> import re
>>> p = re.compile('[a-z]+')
>>> print(p.match(""))
None
>>> p.match("regex")
<re.Match object; span=(0, 5), match='regex'>
>>> m = p.match("regex")
>>> m
<re.Match object; span=(0, 5), match='regex'>

변수 m을 보면 match의 결과가 저장된 것을 볼 수 있다. 저장된 것을 일치 객체(match object)라고 부른다.

이 일치 객체를 조회하여 일치하는 문자열에 대한 정보를 얻을 수 있는 method들이 있다.

method purpose
group() 패턴과 일치하는 문자열을 반환한다.
start() 일치가 시작되는 위치를 반환한다.
end() 일치가 끝나는 위치를 반환한다.
span() 일치의 시작과 끝의 위치를 포함한 tuple을 반환한다.

예시: (위의 코드 블럭에서 이어진다.)

>>> type(m)
<class 're.Match'>
>>> my_string = m.group()
>>> my_string
'regex'
>>> type(my_string)
<class 'str'>
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
>>> print(p.match(":::message"))
None
>>> m = p.search(":::message")
>>> m
<re.Match object; span=(3, 10), match='message'>
>>> m.group()
'message'
>>> m.span()
(3, 10)

 

이스케이프 시퀀스는 정규식에서는 허락되지만, python에서는 인식하지 못해서 DeprecationWarning을 발생시키고 SyntaxError가 일어날 때가 있어서 'r' 접두어가 필요하다고 한다. 하지만 위 사이트에서의 예제의 경우 접두어를 빼도 에러가 나지 않았다.

만약 정규식 활용 중 SyntaxError가 생기면 접두어를 생각해보자.

예시:

>>> import re
>>> p = re.compile(r'\d+')
>>> p.findall("12 people are waiting in 7/11")
['12', '7', '11']
>>> iterator = p.finditer("12 people are waiting in 7/11")
>>> iterator
<callable-iterator object at 0x7ff4e8b2fb50>
>>> for match in iterator:
...     print(match.span())
...
(0, 2)
(22, 24)
(29, 31)

지금까지의 예시들은 객체를 생성한 후에 method들을 호출했다. 하지만 꼭 이렇게 해야 하는 건 아니다. match(), search(), findall(), sub() 등의 최상위 수준 함수도 제공하고 있다.

>>> import re
>>> re.match(r'From\s+', 'From amk Thu May 14')
<re.Match object; span=(0, 5), match='From '>

문자열 수정하기

re 모듈에서는 일치하는 패턴을 찾는 것뿐만 아니라 문자열을 수정하는데도 사용할 수 있다.

사용하는 method들은 아래와 같다.

method purpose
split() 패턴이 일치하는 모든 곳에서 분할하여 문자열을 list로 분할한다.
sub() 패턴이 일치하는 모든 부분 문자열을 찾아 다른 문자열로 대체한다.
subn() sub()와 같은 일을 하지만, 새 문자열과 치환 횟수를 반환한다.

예시:

>>> import re
>>> p = re.compile(r'\W+')
>>> p.split("This is a test, short and sweet, of split().")
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p2 = re.compile(r'(\W+)')
>>> p2.split()
['This', ' ', 'is', ' ', 'a', ' ', 'test', ', ', 'short', ' ', 'and', ' ', 'sweet', ', ', 'of', ' ', 'split', '().', '']
>>> p3 = re.compile('(blue|white|red)')
>>> p3.sub("colour", "blue socks and red shoes")
'colour socks and colour shoes'
>>> p3.subn("colour", "blue socks and red shoes")
('colour socks and colour shoes', 2)

정규식보다 문자열 method?

때때로 re 모듈을 사용하는 것은 실수입니다. 고정된 문자열이나 단일 문자 클래스와 일치시키려고 하고,
IGNORECASE 플래그와 같은 re 기능을 사용하지 않는다면, 정규식의 모든 기능이 필요하지 않을 수 있습니다.
문자열은 고정 문자열을 사용하는 연산을 수행하는 몇 가지 메서드를 가지고 있으며, 대개 훨씬 빠릅니다.
더 크고, 더 일반화된 정규식 엔진 대신, 구현이 목적에 맞게 최적화된 단일하고 작은 C 루프이기 때문입니다.
...(중략)...
문자열에서 단일 문자를 모두 삭제하거나 다른 단일 문자로 바꾸는 것입니다. re.sub('\n', ' ', S)와 같은 방식으로 이
작업을 수행할 수 있지만, translate()는 두 가지 작업을 모두 수행 할 수 있으며 정규식 연산보다 빠릅니다.

위 사이트의 마지막 부분에서 나온 설명이다. 내 개인적으로 정규식을 사용하는 게 뭔가 멋져보인다. 하지만 어떤 작업에 있어서는 문자열에서 제공하는 method를 사용하는게 더 빠를 때도 있다는 것이다.

728x90

'개념 정리' 카테고리의 다른 글

영상 처리의 세 가지 기본 연산  (2) 2022.09.21
Git(2)  (0) 2022.07.06
Git(1)  (0) 2022.07.05
Python의 list, tuple, dictionary, set  (0) 2022.07.02
wsgiref 모듈과 같이 WSGI 알아보기(Python)  (0) 2022.06.25