[파이썬] 정규표현식

osdsoonhyun·2022년 1월 31일
0

파이썬

목록 보기
10/10
post-thumbnail

정규 표현식이란?

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

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

park 800905-1049118
kim 700905-1059119
...

주민등록번호의 뒷부분을 '*'로 바꾸어줄 때 기존 프로그램 코드로 바꾸려면 복잡하고 오래 걸리나 정규표현식을 이용하면 매우 간단하다

>>> import re

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

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

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

정규표현식은 어떠한 문자열의 규칙을 찾아서 일치하는 것과 바꿀 때 주로 사용하게 된다 그래서 이 문자가 어떤 규칙에 매치가 되는지 검사하는 여러가지 수식들을 알아보자

문자 클래스[]

>>>[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"문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치되지 않는다

반복(*)

바로 앞 문자가 여러번 반복되는 표현

>>> ca*t
  • "ct"는 "a"가 0번 반복된 것으로 인식하여 매치
  • "cat"는 "a"가 0번 이상 반복되어 매치(1번 반복)
  • "caaat"는 "a"가 0번 이상 반복되어 매치(3번 반복)

반복(+)

바로 앞 문자가 여러번 반복되는 표현

>>> ca*t
  • "ct"는 "a"가 0번 반복되어 매치되지 않음
  • "cat"는 "a"가 1번 이상 반복되어 매치(1번 반복)
  • "caaat"는 "a"가 1번 이상 반복되어 매치(3번 반복)

반복({m,n},?)

>>> ca{2}t
  • "cat"는 "a"가 1번만 반복되어 매치되지 않음
  • "caat"는 "a"가 2번 반복되어 매치

반복({m,n},?)

"a"가 m이상 n이하 반복되면 매치

>>> ca{2,5}t
  • "cat"는 "a"가 1번만 반복되어 매치되지 않음
  • "caat"는 "a"가 2번 반복되어 매치
  • "caaaaat"는 "a"가 5번 반복되어 매치

반복({m,n},?)

? == {0,1}와 같은 표현

>>> ab?c
  • "abc"는 "b"가 1번만 사용되어 매치
  • "ac"는 "b"가 0번 사용되어 매치

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

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

이런 'p'를 패턴 객체라고 한다 패턴 객체에는 4가지가 있다

  • Match
  • Search
  • Findall
  • Finditer

match

>>> import re
>>> p = re.compile('[a-z]+') //a부터 z까지 1번 이상 반복 되는 것을 찾음
>>>
>>> m = p.match('python')
>>> print(m)
<re.Match object; span=(0, 6), match='python'>

>>> m = p.match("3 python")
>>> print(m)
None

search는 mathch와 달리 첫 번째가 일치하지 않아도 일치하는 구문이 있다면 실행 되었을 때 일치하는 것을 찾아서 return 해준다

>>> import re
>>> p = re.compile('[a-z]+')
>>> m = p.search('python')
>>> print(m)
<re.Match object; span=(0, 6), match='python'>

>>> m = p.search("3 python") 
>>> print(m) 
<re.Match object; span=(2, 8), match='python'>

findall, finditer

findall은 일치하는 string을 리스트에 담아서 return해준다
finditer를 통해 담긴 결과를 for문을 통해 하나씩 출력해보면 match객체가 들어 있는 것을 확인할 수 있다

>>> import re
>>> p = re.compile('[a-z]+')
>>> m = p.findall('life is too short') 
>>> print(m)
['life','is','too','short']

>>> m = p.finditer('life is too short')
>>> for r in m:
>>> 	print(r)
<callable_iterator object at 0x025f50B8>
//match되는 문자열을 다 match 객체 형태로 반복가능한 객체 하나로 return
<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'>

match 객체의 메서드1

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
>>> print(m.span())
(0,6)

컴파일 옵션, DOTALL,S

Dot(.) 문자가 줄바꿈 문자도 포함하도록 만드는 옵션
'DOTALL'써도 되고 약어인 'S'만 써도 된다

>>> import re
>>> p = re.compile('a.b')
>>> m = p.match('a\nb')
>>> print(m)
None
>>> p = re.compile('a.b',re.DOTALL) // 또는 re.S
>>> m = p.match('a\nb')
>>> print(m)
<re.Match object; span(0,3), match='a\nb'>

컴파일 옵션, IGNORECASE, I

대소문자를 무시하고 matching할 수 있게 해주는 옵션

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

>>> p = re.compile('[a-b]',re.I)
>>> print(p.match('python'))
<re.Match object; span(0,1), match='P'>
>>> print(p.match('Python'))
<re.Match object; span(0,1), match='P'>
>>> print(p.match('PYTHON'))
<re.Match object; span(0,1), match='P'>

컴파일 옵션, MULTILINE, M

꺽쇄를 맨 처음만이 아닌 각 라인의 처음으로 인식시키는 옵션

>>> import re
>>> p = re.compile('^python\s\w+',re.M) 

data = """python noe
life is too short
python two
you need python
python three"""

>>> print(p.findall(data))  //'re.M'옵션 주기 전
['python one']
>>> print(p.findall(data))
['python one','python two','python three']

컴파일 옵션, VERBOSE, X

정규표현식이 있을 때 나눠서 쓸 수 있게 만들어주는 옵션
원래 줄바꿈으로 나누게 되면 컴파일이 안되나, 공백들을 제거해줘서 컴파일 할 수 있게 해주는 옵션

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

백슬래시 문제

백슬랴시가 있는 표현식이 있을 때 'r'을 붙여서 로우스트링으로 백슬래시 두 개만 붙여서 이게 공백이 아니다만 표현해주면 정상적으로 동작 가능

>>> \section  // '\s'는 공백을 나타내므로 공백+ection이라 인식 
>>> p = re.compile('\\section') // '\\'가 '\'로 치환되므로
>>> p = re.compile('\\\\section') // 이렇게 해야 정상인식
>>> p = re.compile(r'\\section') // '\\\\'는 너무 길다

강력한 정규 표현식의 세계

메타문자 |

>>> import re
>>> p = re.compile('Crow|Servo') // '|' 또는 으로 이용됨
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0,4), match='Crow'>

메타문자 ^

전까지는 compile한 다음에 compile에서 나온 패턴 객체를 이용하여search를 썼으나 이거를 한번에 할 수 있다

>>> import re
>>> print(re.search('^Life', 'Life is too short')) //(compile할 정규표현식, 찾을 문자열)
<re.Match object; span=(0,4), match='Life'>

>>> print(re.search('^Life', 'My Life'))
None // My가 맨 처음 나와 불일치

메타문자 $

맨 끝을 의미

>>> import re
>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12,17), match='short'> // short이 맨끝에 있어 일치

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

메타문자 \b

>>> import re
>>> p = re.compile('r\bclass\b')
>>> print(p.search('no class at all')) // class 앞뒤로 공백이 있어 일치
<re.Match object; span=(3,8), match='class'>

>>> print(p.search('the declassified algorithm'))
None
>>> print(p.search('one subclass is'))
None

그룹핑1

()를 이용하여 어떤 표현식을 묶어준다

>>> import re
>>> p = re.compile('(ABC)+') // 'ABC+''C'만 반복되는 걸 찾음
>>> m = p.search('ABCABCABC OK?')
>>> print(m)
<re.Match object; span=(0,9), match='ABCABCABC'>

>>> print(m.group())
ABCABCABC
>>> import re
>>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+") 
>>> m = p.search("park'010-1234-1234")
>>> print(m.group(1)) 
park 

정규표현식 중에 첫번째를 따로 불러올 수 있다

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

\1은 앞의 결과를 써주는 것 그룹의 결과를 정규표현식 내부에서 사용 할 수 있다

>>> 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)')
>>> p.search('Paris in the the spring').group()
the the

특정 그룹에 이름을 붙였으면 word라는 그룹을 뒤쪽에 불러 올 수 있다

전방탐색: 긍정형(?=)

여기서 ':'을 빼고 'http'만 표시하고 싶다 검색 조건에는 넣고 검색 결과에서는 빼야 한다

>>> import re
>>> p = re.compile(".+(?=:)") //".+:"
>>> m = p.search("http://google.com")
>>> print(m.group()) 
http // http: 

전방탐색: 부정형(?!)

'bat'가 끝이 포함되지 않게 해달라는 표현

>>> import re
>>> p = re.compile("".*[.](?!bat$exe$).*$", re.M)
>>> l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
>>> print(1) 
['autoexec.jpg']

문자열 바꾸기 sub

정규표현식에 일치하는 것을 .sub 안에 것으로 바꿔줄 수 있다

>>> import re
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
colour socks and colour shoes

Greedy vs Non-Greedy

>>> import re
>>> s = '<html><head><title>Title</title>'
>>> print(re.match('<.*>',s).group()) #Greedy
<html><head><title>Title</title>
>>> print(re.match('<.*>',s).group()) #Non-Greedy
<html> // 반복되는 메타문자 뒤쪽에 물음표를 써 최소한으로 반복시킨다

0개의 댓글