Python #32 | 정규 표현식_정규 표현식 I

신승호·2021년 3월 31일
0

Python

목록 보기
33/35
post-custom-banner

List

  • 정규 표현식
    • 정규 표현식 I
    • 정규 표현식 II

정규 표현식_정규 표현식 I


정규 표현식(Regular Expression)

정규 표현식이란 ?

  • 복잡한 문자열을 처리하기 할때 사용하는 기법

    ※ 정규 표현식은 줄여서 간단히 "정규식"이라고도 말한다.

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

메타문자(meta characters)

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

문자 클래스(character class) [ ]

  • 문자클래스 [ ] : "[ ] 사이의 문자들과 매치"`

    • ※ 문자 클래스를 만드는 메타 문자인 [ ] 사이에는 어떤 문자도 들어갈 수 있다.
  • 문자 클래스 [ ] 사용법

    • [abc] = "a, b, c중 한 개의 문자와 매치"
문자열 "a", "before", "dude"가 정규식 [abc]와 어떻게 매치되는가 ? 

- "a"는 매치	# 정규식과 일치하는 문자 "a"가 있기 때문
- "before"은 매치	# 정규식과 일치하는 문자 "b"가 있기 떄문
- "dude" 는 매치불가	# 정규식과 일치하는 문자인 a,b,c중 아무것도 없기 떄문
  • 문자 클래스 [문자1-문자2] : [문자1(from)-문자2(to)]안 두문자 사이의 문자들과 매치"`
   - `[a-c]` = `[abc]` 
   - `[0-5]` = `[012345]`
   - `[a-zA-Z]` = `[알파벳 모두]`
   - `[0-9]` = `[0,1,2,3,4,5,6,7,8,9]`

[문자 클래스 사용시 주의할 점]

  • ^ : 문자 클래스 안에 매타 문자 ^를 사용할 경우에는 반대(not)라는 의미를 갖는다.
[^0-9] = [0,1,2,3,4,5,6,7,8,9] 숫자가 아닌 문자와만  매치

[자주 사용하는 문자 클래스]

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

Dot(.)

  • Dot(.) : \n(줄바꿈 문자)를 제외한 모든문자와 매치

    re.DOTALL 옵션을 주면 \n과도 매치된다.

  • Dot(.) 사용법
    • a.b= "a + 모든문자 + b", a와 b사이 어떤 문자가 들어가도 전부 매치된다.
- "aab" 는 매치		# 가운데 문자 "a"가 모든 문자를 의미하는 .과 일치
- "a0b" 는 매치		# 가운데 문자 "0"가 모든 문자를 의미하는 .과 일치
- "abc"는 매치불가		# "a"문자와 "b"문자 사이에 문자가 하나도 없다.
  • Dot(.) 사용법 2
    • a[.]b = "a + Dot(.)문자 + b", 문자 클래스 안에 . 이 있으므로 a와 b사이 무조건 .만 올수 있다.
 - "a.b"은 매치		
 - "a0b"은 매치불가 

※ 만약 문자 클래스([]) 내에 Dot(.) 메타 문자가 사용된다면 이것은 "모든 문자"라는 의미가 아닌 문자 . 그대로를 의미한다.

반복 (*)

  • 반복 * : *바로 앞에 있는 문자가 0부터 무한대로 반복되야 매치
  • 반복 * 사용법
    • ca*t = *앞에있는 문자 a가 무한대로 반복 될수 있다.(반복이 없어도 매치되는거당)
- "ct"는 매치		# "a"가 0번 반복
- "cat"는 매치		# "a"가 0번 이상 반복(1회 반복)
- "caaat"는 매치		# "a"가 0번 이상 반복(3회 반복)

반복 (+)

  • 반복 + : +바로 앞에 있는 문자가 최소 1번 이상 반복되야 매치
  • 반복 + 사용법
    • ca*t = +앞에있는 문자 a가 최소 1번 이상 될 수 있다.(0개 반복은 매치불가)
- "ct"는 매치불가		# "a"가 0번 반복
- "cat"는 매치		# "a"가 1번 이상 반복(1회 반복)
- "caaat"는 매치		# "a"가 1번 이상 반복(3회 반복)

*는 반복 횟수 0부터 / +는 반복 횟수 1부터 인 것이다.

반복 ({m,n}, ?)

  • 반복 ({m,n}, ?) : 반복 횟수를 고정하 수 있다.

    • {1,3} : 1 ~ 3 반복
    • {3,} : 반복 횟수 3 이상
    • {,3} : 반복 횟수 3 이하

      {1,} = + , {0,} = *

  • 반복 ({m,n}, ?) 사용법

    • {m} = 반드시 m 번 반복

      • ca{2}t = "c + a(반드시 2번 반복) + t"
      -"cat" 는 매치불가		# "a"가 1번 반복
      -"caat" 는 매치		# "a"가 2번 반복
      
    • {m, n} = m~n반복

      • ca{2,5}t = "c + a(2~5회 반복) + t"
      -"cat" 는 일치 불가	# "a"가 1번만 반복
      -"caat" 는 일치		# "a"가 2번 반복
      -"caaaaat"는 일치		# "a"가 5번 반복
      
    • ? = {0, 1}

      • ab?c = "a + b(있어도 되고 없어도 된다) + c"
      - "abc"는 매치		# "b"가 1번 사용
      - "ac"는 매치		# "b"가 0번 사용

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

re(regular expression) 모듈

  • 자동으로 설치되는 기본 라이브러리이다
  • re(regular expression) 모듈 사용법
** 이 자체가 패턴이당
>>> import re
>>> p = re.compile('ab*')	# ab*를 컴파일 하고, 객체 p를 사용하여 이후 작업을 수행할 것이다
  • 정규식을 컴파일할 때 특정 옵션을 주는 것도 가능

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

패턴 생성

  • 패턴 = 정규식을 컴파일한 결과
** 이 자체가 패턴이다. 

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

match

  • 문자열의 처음부터 정규식과 매치되는지 조사
    • 정규식과 매치될 때 match객체를 돌려주고,
    >>> m = p.match("python")		# "python"은 "[a-z]+정규식과 매치 된다.
    >>> print(m) 
    <_sre.SRE_Match object at 0x01F3F9F8>	# 그렇기 때문에 match 객체(m)를 돌려준다
    • 정규식과 매치되지 않을 때 None을 돌려준다,
    >>> m = p.match("3 python")		# 숫자 3 때문에 매치불가
    >>> print(m)
    None					# None을 돌려준다
  • match()사용법
 p = re.compile(정규표현식)		# 여기 안에 정규 표현식이 들어가야지~[a-z]+ 이런거 ~ 
m = p.match( 'string goes here' )
if m:					# match의 결괏값이 있을 때만 그다음 작업을 수행하겠다는 것
    print('Match found: ', m.group())
else:
    print('No match')
  • 문자열 전체를 검색하여 정규식과 매치되는지 조사
>>> m = p.search("python")			
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA68>	# match 와 동일
>>> m = p.search("3 python")
>>> print(m)
<_sre.SRE_Match object at 0x01F3FA30>	# match와 다름/ search는 문자열 전체를 검색하기 때문에 "3 " 이후의 "python" 문자열과 매치

match 메서드search 메서드문자열의 처음부터 검색할지의 여부에 따라 다르게 사용해야 한다.

findall

  • 정규식과 매치되는 모든 문자열(substring)리스트로 돌려준다.
>>> result = p.findall("life is too short")	# 문자열의 'life', 'is', 'too', 'short' 단어를 각각 [a-z]+ 정규식과 매치
>>> print(result)
['life', 'is', 'too', 'short']			# 전부 매치되서 결괏값 출력

finditer

  • 정규식과 매치되는 모든 문자열(substring)반복 가능한 객체로 돌려준다.
>>> result = p.finditer("life is too short")	# 
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result: print(r)
...
<_sre.SRE_Match object at 0x01F3F9F8>
<_sre.SRE_Match object at 0x01F3FAD8>
<_sre.SRE_Match object at 0x01F3FAA0>
<_sre.SRE_Match object at 0x01F3F9F8>
  • findall()finditer()은 동일하다.
  • 결괏값을 출력하는 방법이 다를 뿐
    • findall() -> 리스트(list)로 출력
    • finditer()-> 반복 가능한 객체(iterator object)로 출력
      • 반복 가능한 객체의 각 요소들은 match 객체 이다.

match & search 객체의 메서드

메서드 수행결과에 따른 문자열과 인덱싱을 확인하자

  • match()search()객체의 매서드
    • group() : 매치된 문자열을 돌려준다.
    • start() : 매치된 문자열의 시작 위치를 돌려준다.
    • end() : 매치된 문자열의 끝 위치를 돌려준다.
    • span() : 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다.
>>> m = p.match("python")
>>> m.group()
'python'
>>> m.start()		# 슬라이싱 개념으로 생각하자. 
0
>>> m.end()		# 슬라이싱 개념으로 생각하자. 
6
>>> m.span()		# 슬라이싱 개념으로 생각하자. 
(0, 6)		
>>> m = p.search("3 python")
>>> m.group()
'python'
>>> m.start()		# 딱 python만 가지고 오는거지, 슬라이싱개 개념으로 생각하자
2
>>> m.end()		# 딱 python만 가지고 오는거지, 슬라이싱개 개념으로 생각하자
8
>>> m.span()		# 딱 python만 가지고 오는거지, 슬라이싱개 개념으로 생각하자
(2, 8)

[모듈 단위로 수행]

  • re 모듈 축약 해보자 ㅡ ㅡ
>> p = re.compile('[a-z]+')
>> m = p.match("python")
>> m = re.match('[a-z]+', "python")
  • 보통 한 번 만든 패턴 객체를 여러번 사용해야 할 때는 이 방법보다 re.compile을 사용하는 것이 편하다.

컴파일 옵션

DOTALL, S

  • re.DOTALL = re.S
    • . 메타문자에 \n는 제외되지만, \n을 포함 시키고 싶다면 re.DOTALL 또는 re.S 옵션을 줘라
  • 기존 패턴
    >>> import re
    >>> p = re.compile('a.b')
    >>> m = p.match('a\nb')		# 메타문자 . 을 사용했을 때 \n 은 매치 되지 않는다
    >>> print(m)
    None
  • re.DOTALL 옵션 사용
    >>> p = re.compile('a.b', re.DOTALL)	# re.DOTALL 과 re.S 아무거나 사용하면 됌
    >>> m = p.match('a\nb')
    >>> print(m)
    <_sre.SRE_Match object at 0x01FCF3D8>

IGNORECASE, I

  • re.IGNORECASE = re.I
    • 대소문자 구별 없이 매치를 수행
    >>> p = re.compile('[a-z]', re.I)	# [a-z]은 소문자만 의미 / re.I 옵션으로 대소문자 구별없이 매치
    >>> p.match('python')
    <_sre.SRE_Match object at 0x01FCFA30>
    >>> p.match('Python')
    <_sre.SRE_Match object at 0x01FCFA68>
    >>> p.match('PYTHON')
    <_sre.SRE_Match object at
    0x01FCF9F8>

MULTILINE, M

  • re.MULTILINE = re.M
    • 문자열이 여러줄 일 때, 각 각의 줄을 하나의 문자열로 여기도록 하는거
    • 위의 옵션은 ^, $ 와 연관이 있다.

[^, $ 의 사용]

  • ^는 문자열의 처음을 의미
    • ^python ~인 경우, 문자열의 처음은 항상 python으로 시작해야 매치된다.
  • $는 문자열의 마지막을 의미
    • ~ python$인 경우, 문자열의 마지막은 항상 python으로 끝나야 매치된다.
  • 기존 패턴
import re
p = re.compile("^python\s\w+")		# "python"이라는 문자열로 시작, 그 뒤 "whitespace", 그 뒤 "단어"
data = """python one			# data 는 여러 줄로 이루어져 있음
life is too short
python two
you need python
python three"""

print(p.findall(data))
['python one']		# 메타 문자 "^" 때문 python으로 시작하는 문자열인 첫번째 문자열만 출력
  • 옵션 사용
import re
p = re.compile("^python\s\w+", re.MULTILINE)	# re.MULTILINE 옵션으로 인해 메타 문자 "^" 가 문자열 전체가 아닌 각 줄의 처음부터 보라고 하는것

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

print(p.findall(data))
['python one', 'python two', 'python three']

VERBOSE, X

  • re.VERBOSE = re.X
    • 문자열에 사용된 whitespace는 컴파일할 때 제거된다 (*단 [ ] 안에 사용한 whitespace는 제외)
    • 이해하기 어려운 정규식을 주석 또는 줄 단위로 구분 할때 사용한다.
    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)

백슬래시 문제

"\section" 문자열을 찾기 위한 정규식

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

  • r' 활용
    • 컴파일해야 하는 정규식이 Raw String임을 알려주는 파이썬 문법
    • 위와 같이 진행 시 \section을 찾도록 해준당
  • p = re.compile('\\\\section')
    • 성공
    • 이거도 작동 된다
  • p = re.compile('\section')
    • 탈락
    • \section = [ \t\n\r\f\v]ection
    • \를 한번 썻을 떄 앞의 \swhitespace로 해석되어 원하는 게 나오지 않는다.
  • p = re.compile('\\section')
    • 탈락
    • 문자열 자체임을 알려 주기 위해 \\ 사용하여 이스케이프 처리 시도 머리좀 씀
    • 하지만 파이썬 정규식 엔진에는 파이썬 문자열 리터럴 규칙에 따라 \\\로 변경되어 \section이 전달된다.
    • 결국 p = re.compile('\section') 이럭게 된당
profile
신승홉니다
post-custom-banner

0개의 댓글