본문 바로가기

코딩/파이썬

[점프 투 파이썬] 정규 표현식(1) 메타문자종류

정규 표현식이란?

복잡한 문자열을 처리할 때 사용하는 기법, 파이썬 뿐 아니라 문자열을 처리하는 모든 곳에서 사용함


정규 표현식이 필요한 이유는?

정규 표현식을 알고 있으면 코드를 더 간결하게 짤 수 있다.

ex) 예시로 다음 문제를 푼다고하자

주민등록번호를 포함하는 텍스트 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 *로 변경해보자

위의 문제를 풀기 위해서는 정규식을 모를 경우 다음과 같은 순서로 프로그램을 작성한다.

  1. 전체 텍스트를 공백 문자로 나눈다.
  2. 나뉜 단어가 주민등록번호 형식인지 조사한다.
  3. 단어가 주민등록번호 형식이라면 뒷자리를 *로 변환한다.
  4. 나뉜 단어를 다시 조합한다.

반면 정규식을 알고 있다면 간단하게 코드를 작성할 수 있다.

import re

data= """
park 800905-1049118
kim  700905-1059119
"""

pat=re.compile("(\\d{6}[-]\\d{7})")
print(pat.sub("\\g<1>-*******",data))

[결과]
park 800905-1049118-*******
kim  700905-1059119-*******

 

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

1. 문자 클래스[ ]:

  • [ ] 사이의 문자들과 매치라는 의미를 갖는다.
  • 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위를 의미한다.
    • ex) [a-c]==[abc], [a-zA-Z]==[알파벳 모두], [0-9]==[숫자]
  • [ ]안에는 어떤 메타 문자도 사용가능하지만 (^)는 반대의 으미를 갖는다.
    • ex) [^0-9]==숫자가 아닌 문자

자주 사용하는 정규 표현식

정규 표현식 설명

\d 숫자와 매치
\D 숫자가 아닌 것과 매치
\s whitespace 문자(공백을 표현하는 문자)와 매치
\S whitespace가 아닌 것과 매치
\w 문자+숫자와 매치
\W 문자+숫자가 아닌 것과 매치

2. Dot(.):

  • 줄바꿈 문자인 \n을 제외한 모든 문자와매치
  • a.b과 a[.]b 정규 표현식은 다르다.
    • a.b 표현식은 a와b사이에 줄바꿈 문자 빼고 다 올 수 있음
    • a[.]b는 ‘a.b’문자열과 매치되는거

3. 반복(*):

    • 바로 앞에 있는 문자 a가 0부터무한대로 반복될 수 있다는 의미
      • ex) ca*t 정규식에선 문자열 ct,cat,caaat모두 매치된다.
  • 최소 1번 이상 반복될때는 +를 사용한다.
  • {m,n}을 사용하면 반복 횟수를 고정 할 수 있다.
    • {1,3}: 1번 이상 3번 이하 반복
    • {3,}: 최소 3번 반복
    • {,3}: 최대 3번 반복
    • {m}: m번 반복 만
  • ? : {0,1}과 같은 의미로 사용된다.

4. |

|는 or과 같은 의미로 A|B 정규식에서 A혹은 B라는 의미를 갖는다.

p = re.compile('Crow|servo')
m = p.match('CrowHello')
print(m)

[결과]
<re.Match object; span=(0, 4), match='Crow'>

5.^

^ 메타 문자는 문자열의 맨 처음과 일치함을 의미

print(re.search('^Life','Life is too short'))
print(re.search('^Life','My Life'))

[결과]
<re.Match object; span=(0, 4), match='Life'>
None

6.$

$는 문자열의 끝과 매치함을 의미

print(re.search('short$','Life is too short'))
print(re.search('short$','Life is too short, you need python'))

[결과]
<re.Match object; span=(12, 17), match='short'>
None

7.\A

^메타 문자와 동일한 의미를 갖지만 re.MULTILINE옵션 사용시 다르게 해석

  • ^는 re.MULTILINE옵션 사용시 각 줄의 처음과 일치
  • \A 줄과 상관없이 전체 문자열의 처음하고만 매치
p1 = re.compile("^python\\s\\w+", re.MULTILINE)
p2 = re.compile("\\Apython\\s\\w+", re.MULTILINE)
data= """python one
life is too short
python two
you need python
python three
"""
print(p1.findall(data))
print(p2.findall(data))

[결과]
['python one', 'python two', 'python three']
['python one']

8.\Z

$메타 문자와 동일한 의미를 갖지만 re.MULTILINE옵션 사용시 다르게 해석

p1 = re.compile("\\dshort$", re.MULTILINE)
p2 = re.compile("\\dshort\\Z", re.MULTILINE)
data= """Life is too 1short
life is too short 2short
Life is too short 3short"""
print(p1.findall(data))
print(p2.findall(data))

[결과]
['1short', '2short', '3short']
['3short']

9.\b

\b는 단어구분자이다. 보통 단어는 whitespace에 의해 구분된다.

p = re.compile(r'\\bclass\\b')
print(p.search('no class at all'))
print(p.search('the declassified algorithm'))

[결과]
<re.Match object; span=(3, 8), match='class'>
None

사용시 중요한점은 파이썬 리터럴 규칙에 의해서 벡스페이스를 의미하므로 Raw string임을 알려주는 기호 r을 반드시 붙여 주어야 한다.

10. \B

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

p = re.compile(r'\\Bclass\\B')
print(p.search('no class at all'))
print(p.search('the declassified algorithm'))
print(p.search('one subclass is'))

[결과]
None
<re.Match object; span=(6, 11), match='class'>
None

 


 

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

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

[결과]
re.Pattern

사용 할 때 위와같이 정구 표현식을 re.compile을 통하여 컴파일 한다. 결과로 돌려주는 패턴 객체를 사용하여 작업을 수행한다.


 

정규식을 사용한 문자열 검색

메서드 목적

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

위의 매소드를 사용해 보면

p = re.compile('[a-z]+')

match()

[a-z]+정규식을 써서 match의 결과를 확인해보면

p.match('python')

[결과]
<re.Match object; span=(0, 6), match='python'>

정규식에 부합하므로 match 객체를 반환한다.

print(p.match('3 python'))
print(p.match('Python'))

[결과]
None
None

처음 나오는 대문자와 3은 정규식에 부합되지 않아 None을 돌려줌

match 메서드를 통해 컴파일을 한번에 진행할 수도 있다.

re.match('[a-z]+','python')

[결과]
<re.Match object; span=(0, 6), match='python'>

search()

p.search('3 python')

[결과]
<re.Match object; span=(2, 8), match='python'>

문자열 전체를 검색하기 때문에 3 이후의 문자열이 매치된다.

findall()

p.findall('hello world')

[결과]
['hello', 'world']

각 단어들을 리스트로 반환

finditer()

result=p.finditer('hello 3 world')
print(result)

[결과]
<callable_iterator object at 0x000002AF486BD070>

반복 가능한 객체로 돌려줌

이를 for문을 통해 출력해보면 다음과 같이 나온다.

for r in result:
    print(r)

[결과]
<re.Match object; span=(0, 5), match='hello'>
<re.Match object; span=(8, 13), match='world'>