[점프투파이썬] 7장 - 정규 표현식 정리

해롱그·2023년 6월 16일
0

파이썬

목록 보기
8/12
post-thumbnail

정규 표현식

복잡한 문자열을 처리할 때 사용하는 기법, 모든 언어 공통으로 사용됨

정규 표현식은 왜 필요한가?

💡 example

import re

data = """
park 800905-1049118
kim  790102-1039485
"""

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

>>> park 800905-*******
	kim  790102-*******

이런 문자열에 관련된 복잡한 문제를 해결 해야될 때 정규 표현식을 사용하면 짧고 간결하게 문제를 해결할 수 있음!

정규 표현식 시작하기

어떤 문자열의 규칙을 찾아서 어떤 거와 일치하는 것을 뭐로 바꿔라~ 이런 문제를 처리할 때 사용함!
그래서 이 문자가 어떤 규칙에 매치가 되는지 검사하는 여러가지 수식들이 있다.

문자 클래스 [ ]

[abc]

  • [] 안의 문자들과 매치하는지 검사하는 수식
  • "a"는 정규식과 일치하는 문자인 "a"가 있으므로 매치
  • "before"는 정규식과 일치하는 문자인 "b"가 있으므로 매치
  • "dude"는 정규식과 일치하는 문자인 a,b,c 중 어느 하나도 포함하고 있지 않으므로 매치되지 않음
  • 하이픈을 사용하여 From-To로 표현 가능
    Ex) [a-c] = [abc], [0-5] = [012345]

Dot(.)

a.b

  • 줄바꿈(\n)을 제외한 모든 문자와 매치하는지 검사하는 수식
  • "aab"는 가운데 문자 "a"가 모든 문자를 의미하는 '.'과 일치하므로 정규식과 매치
  • "a0b"는 가운데 문자 "0"가 모든 문자를 의미하는 '.'과 일치하므로 정규식과 매치
  • "abc"는 "a"문자와 "b"문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치 X

반복 (*)

ca*t

  • * 바로 앞 문자가 여러번 반복되는지 검사하는 수식
  • "ct"는 "a"가 0번 반복되어 매치
  • "cat"는 "a"가 0번 이상 반복되어 매치 (1번 반복)
  • "caaat"는 "a"가 0번 이상 반복되어 매치 (3번 반복)

반복 (+)

ca+t

  • "ct"는 "a"가 0번 반복되어 매치 X
  • "cat"는 "a"가 1번 이상 반복되어 매치 (1번 반복)
  • "caaat" 는 "a"가 1번 이상 반복되어 매치 (3번 반복)

반복 ({m,n}, ?)

ca{2}t

  • a{2} => a가 딱 2번 반복된다는 의미
  • "cat"는 "a"가 1번 반복되어 매치 X
  • "caat"는 "a"가 2번 반복되어 매치

ca{2,5}t

  • 중괄호 앞 문자와 m이상 n이하 반복되면 매치
  • "cat"는 "a"가 1번 반복되어 매치 X
  • "caat"는 "a"가 2번 반복되어 매치
  • "caaaaat" "a"가 5번 반복되어 매치

ab?c

  • ? 앞 문자가 0회 혹은 1회 사용되면 매치
  • "abc"는 "b"가 1번 사용되어 매치
  • "ac"는 "b"가 0번 사용되어 매치
  • ab{0,1}c 와 같은 표현!!!

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

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

p(패턴객체)를 이용해서 우리가 원하는 문자열과 비교 해볼 수 있음!
이용하는 방법은 4가지가 있다

  1. Match
    조건에 정확히 부합할 경우 return
# 매치가 될 경우
import re
p = re.compile('[a-z]+') #a부터 z까지의 어떤 문자열이 1번 이상 반복되는 표현
m = p.match('python')
print(m)

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

# 매치가 안될 경우
import re
p = re.compile('[a-z]+') #a부터 z까지의 어떤 문자열이 1번 이상 반복되는 표현
m = p.match('3python')
print(m)

>>> None
  1. Search
    search의 경우, '검색하다' 여서 꼭 첫번째가 일치하지 않더라도 일치하는 구문이 존재한다면 일치하는 걸 찾아서 match 객체를 return
import re
p = re.compile('[a-z]+') #a부터 z까지의 어떤 문자열이 1번 이상 반복되는 표현
m = p.search('3 python')
print(m)

>>> <re.Match object; span=(2, 8), match='python'>
  1. Findall
    일치하는 string을 찾아 list에 담아서 return
import re
p = re.compile('[a-z]+') #a부터 z까지의 어떤 문자열이 1번 이상 반복되는 표현
m = p.findall('Life is too short')
print(m)

>>> ['ife', 'is', 'too', 'short']
  1. Finditer
    매치되는 문자열을 다 match 객체 형태로, 반복 가능한 객체 하나로 return
import re
p = re.compile('[a-z]+') #a부터 z까지의 어떤 문자열이 1번 이상 반복되는 표현
m = p.finditer('Life is too short')
for r in m:
    print(r)
    
>>> <re.Match object; span=(1, 4), match='ife'>
	<re.Match object; span=(5, 7), match='is'>
	<re.Match object; span=(8, 11), match='too'>
	<re.Match object; span=(12, 17), match='short'>

match 객체의 메서드

method목적
group()매치된 문자열을 리턴한다.
start()매치된 문자열의 시작 위치를 리턴한다.
end()매치된 문자열의 끝 위치를 리턴한다.
span()매치된 문자열의 (시작, 끝)에 해당되는 튜플을 리턴한다.
import re
p = re.compile('[a-z]+')
m = p.match('python')

print(m.group())	#python
print(m.start())	#0
print(m.end())		#6 -> 끝 인덱스는 5, 그 다음 수 출력
print(m.span())		#(0,6)

컴파일 옵션 - DOTALL, S

줄바꿈 문자도 포함하도록 만드는 옵션

import re
p = re.compile('a.b')
m = p.match('a\nb')
print(m)	#None

# DOTALL, S
import re
p = re.compile('a.b', re.DOTALL)
m = p.match('a\nb')
print(m)

>>> <re.Match object; span=(0, 3), match='a\nb'>

컴파일 옵션 - IGNORECASE, I

대소문자를 무시하고 매칭할 수 있도록 해주는 옵션

import re
p = re.compile('[a-z]')
print(p.match('python'))	#<re.Match object; span=(0, 1), match='p'>
print(p.match('Python'))	#None
print(p.match('PYTHON'))	#None

# IGNORECASE, I
import re
p = re.compile('[a-z]', re.IGNORECASE)
print(p.match('python'))
print(p.match('Python'))
print(p.match('PYTHON'))

>>> <re.Match object; span=(0, 1), match='p'>
	<re.Match object; span=(0, 1), match='P'>
	<re.Match object; span=(0, 1), match='P'>

컴파일 옵션 - MULTILINE, M

꺽쇠(^)를 맨 처음만이 아닌 각 라인의 처음으로 인식시키는 옵션
^ : 맨 처음
\s : 공백을 나타내는 문자
\w : 알파벳, 숫자, _ 중 한 문자

# 멀티라인 옵션 안줬을 때
import re
p = re.compile('^python\s\w+')  #맨 처음에 python이라는 글자가 나오고 그 다음 공백, 그리고 word(w)가 여러번 반복되는 것

data = """python one
life if too short
python two
python three"""

print(p.findall(data))

>>> ['python one']		#맨 처음 문장만 리턴

# 멀티라인 옵션을 준 경우
import re
p = re.compile('^python\s\w+', re.MULTILINE)  #맨 처음에 python이라는 글자가 나오고 그 다음 공백, 그리고 word(w)가 여러번 반복되는 것

data = """python one
life if too short
python two
python three"""

print(p.findall(data))

>>> ['python one', 'python two', 'python three']

컴파일 옵션 - VERBOSE, X

긴 정규표현식이 있을 때 나눠서 쓸 수 있게 만들어주는 옵션
정규표현식을 줄바꿈 할 경우 원래는 컴파일이 안되지만, verbose를 사용하면 공백들을 제거해줌!

import re
charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')

charref = re.compile(r"""
 &[#]               # Start of a numeric entity reference
 (
      0[0-7]+       # Octal form
    | [0-9]+        # Decimal form
    | x[0-9a-fA-F]+ # Hexadecimal form
 )
 ;                  # Trailing semicolon
""", re.VERBOSE)

백슬래시 문제

r(로우스트링) 이용하여 \ 그대로 사용!
💡 example

\section
p = re.compile('\\section')
p = re.compile('\\\\section')
p = re.compile(r'\\section')

\s : 공백을 나타내는 문자이므로 (공백)ection으로 표시됨
따라서 \section을 입력해준다. but \ -> \ 로 표현되는 문제 발생.
따라서 \\section을 입력해줘야 원하는 \section 으로 표시됨!

이런 번거로움이 있을 때 사용하는 r
백슬래시가 있는 어떤 표현식이 있을 때 r(로우스트링)을 붙여서 백슬래시 두개만 붙여서 이게 공백이 아니다!만 표현해주면 정상적으로 동작할 수 있음

강력한 정규 표현식의 세계로

메타문자

  1. |
    or 의 의미!
import re
p = re.compile('Crow|Servo')	#Crow 또는 Servo와 일치하는가
m = p.match('CrowHello')
print(m)

>>> <re.Match object; span=(0, 4), match='Crow'>
  1. ^
    맨 처음과 일치하는지!
import re
print(re.search('Life', 'Life is too short'))
print(re.search('^Life', 'My Life'))

>>> <re.Match object; span=(0, 4), match='Life'>
	None
  1. $
    맨 끝을 의미!
import re
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
  1. \b
    공백을 나타내는 메타문자
import re
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'))

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

그루핑 ()

괄호를 이용해서 어떤 표현식을 묶어주는 것

import re
p = re.compile('ABC+')		#C만 반복
m = p.search('ABCABCABC OK?')
print(m)
print(m.group())

>>> <re.Match object; span=(0, 3), match='ABC'>
	ABC

# 그루핑 할 경우
import re
p = re.compile('(ABC)+')
m = p.search('ABCABCABC OK?')
print(m)
print(m.group())

>>> <re.Match object; span=(0, 9), match='ABCABCABC'>
	ABCABCABC

추가로 그룹핑된 문자열에 이름을 따로 붙일수도 있음!

import re
p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
m = p.search("park 010-1234-1234")
print(m.group("name"))

>>> park

import re
p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
print(p.search('Paris in the the spring').group())

>>> the the

전방탐색

  1. 긍정형 (?=)
    검색 조건에는 포함되나, 결과에는 포함되지 않게 하기 위해 사용!
import re
p = re.compile('.+:')
m = p.search("http://google.com")
print(m.group())

>>> http:

위 식에서 ' : ' 을 검색 조건에는 넣되, 결과에서는 빼고 싶은 경우

import re
p = re.compile('.+(?=:)')		# ?=원하는 문자열
m = p.search("http://google.com")
print(m.group())

>>> http
  1. 부정형 (?!)
import re
p = re.compile(".*[.](?!bat$).*$", re.M)    # bat가 끝에 포함되지 않는 것 출력
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)

>>> ['autoexec.exe', 'autoexec.jpg']

import re
p = re.compile(".*[.](?!bat$|exe$).*$", re.M)    # bat 또는 exe가 끝에 포함되지 않는 것 출력
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)

>>> ['autoexec.jpg']

문자열 바꾸기 sub

sub('바꿀 문자열', '해당 문장'), 해당 문장에 정규표현식 조건에 부합한 문자열이 있으면 바꿀 문자열로 변경!

import re
p = re.compile('(blue|white|red)')
a = p.sub('colour', 'blue socks and red shoes')     #정규표현식에 일치하는 것을 colour로 바꿔줌
print(a)

>>> colour socks and colour shoes

Greedy vs Non-Greedy

Greedy: 탐욕스러운

import re
s = '<html><head><title>Title</title>'
print(re.match('<.*>', s).group())  	#Greedy

>>> <html><head><title>Title</title>	#모든걸 가져오는 탐욕스러운 결과를 나타냄

Non-Greedy
반복되는 메타문자 뒤쪽에 물음표를 써주는 방식

import re
s = '<html><head><title>Title</title>'
print(re.match('<.*?>', s).group())     #Non-Greedy, 물음표는 최소한으로 반복하겠다는 의미!

>>> <html>		#<.*?> -> 최소한으로 반복된 다음에 닫히므로 <html> 하나만 출력됨

Reference
참고한 영상

profile
사랑아 컴퓨터해 ~

0개의 댓글