코딩하는 타코야끼

[Python] 11강_파이썬 정규표현식 본문

[T.I.L] : Today I Learned/Python

[Python] 11강_파이썬 정규표현식

가스오부시 2023. 4. 9. 20:44
728x90
반응형

1. 정규 표현식(Regular Expression) 개요

📍 정규 표현식이란

  • 텍스트에서 특정한 형태나 규칙을 가지는 문자열을 찾기 위해 그 형태나 규칙을 정의하는 것.
  • 파이썬 뿐만 아니라 문자열을 다루는 모든 곳에서 사용된다.
  • 정규식, Regexp이라고도 한다.

📍 기본개념

  • 패턴
    • 정규 표현식이라고 한다.
    • 문장내에서 찾기위한 문구의 형태에 대한 표현식.
  • 메타문자
    • 패턴을 기술하기 위해 사용되는 특별한 의미를 가지는 문자
    • 예) a* : a가 0회 이상 반복을 뜻한다. a, aa, aaaa
  • 리터럴
    • 표현식이 값 자체를 의미하는 것
    • 예) a는 자체를 의미한다.

2. 정규 표현식 메타 문자

  • 패턴을 기술하기 위한 문자

📍 문자 클래스 : [ ]

  • [ ]사이의 문자들과 매칭
    • [abc] : a, b, c 중 하나의 문자와 매치
  • ``를 이용해 범위로 설정할 수 있다.
    • [a-z] : 알파벳소문자중 하나의 문자와 매치
    • [a-zA-Z0-9] : 알파벳대소문자와 숫자 중 하나의 문자와 매치
  • [^ 패턴] : ^ 으로 시작하는 경우 반대의 의미
    • [^abc] : a, b, c를 제외한 나머지 문자들 중 하나와 매치.
    • [^a-z] : 알파벳 소문자를 제외한 나머지 문자들 중 하나와 매치

📍 미리 정의된 문자 클래스

  • 자주 사용되는 문자클래스를 미리 정의된 별도 표기법으로 제공한다.
  • \d : 숫자와 매치. [0-9]와 동일
  • \D : \d의 반대. 숫자가 아닌 문자와 매치. [^0-9]와 동일
  • \w : 문자와 숫자, _(underscore)와 매치. [a-zA-Z0-9_]와 동일
  • \W : \w의 반대. 문자와 숫자와 _ 가 아닌 문자와 매치. [^a-zA-Z가-힣0-9_]와 동일
  • \s : 공백문자와 매치. tab,줄바꿈,공백문자와 일치
  • \S : \s와 반대. 공백을 제외한 문자열과 매치.
  • \b : 단어 경계(word boundary) 표시. 보통 단어 경계로 빈문자열
    • 단어경계: 단어(글자- \\w)와 단어가 아닌 문자사이를 가리킨다.
    • \\b가족\\b => 우리 가족 만세(O), 우리가족만세 (X)
  • \B : \b의 반대. 단어 경계로 구분된 단어가 아닌 경우
    • \B가족\B => 우리 가족 만세(X), 우리가족만세 (O)

📍 글자수와 관련된 메타문자

  •   .  : 한개의 모든 문자(\n-줄바꿈 제외) (a.b)
  • `` : 앞의 문자(패턴)과 일치하는 문자가 0개 이상인 경우. (a*b)
  • + : 앞의 문자(패턴)과 일치하는 문자가 1개이상인 경우. (a+b)
  • ? : 앞의 문자(패턴)과 일치하는 문자가 한개 있거나 없는 경우. (a?b)
  • {m} : 앞의 문자(패턴)가 m개. (a{3}b)
  • {m,} : 앞의 문자(패턴)이 m개 이상. (a{3,}b)
    • , 뒤에 공백이 들어오지 않도록 한다.
  • {m,n} : 앞의 문자(패턴)이 m개이상 n개 이하. (a{2,5}b)
  • ., ``, +, ? 를 리터럴로 표현할 경우 \\를 붙인다.

📍 문장의 시작과 끝 표현

  • ^ 문자열의 시작 (^abc)
    • 문자 클래스([ ])의 ^와는 의미가 다르다.
  • $ : 문자열의 끝 (abc$)

📍  기타

  • : 둘중 하나 (OR) (010|011|016|019)
    • 010|016-111 : 010 또는 016-111 이 된다.
  • ( ) : 패턴내 하위그룹을 만들때 사용

 


3. re 모듈

  • 파이썬에서 정규 표현식을 지원하기 위한 모듈
  • 파이썬 기본 라이브러리

📍 코딩패턴

🌓 모듈 import

  • import re

🌓 객체지향형

  • 패턴 객체를 생성후 메소드를 호출해 원하는 처리를 한다.

🌓 함수형

  • re 모듈의 원하는 작업을 하는 함수를 호출한다. Argument로 패턴과 처리할 값을 전달한다.

🌓  raw string

  • 패턴문자중 \로 시작하는 것들을 사용할 경우 escape 문자와의 구분을 위해 \\ 두개씩 작성해야한다. 그래서 패턴을 지정할 때는 raw string을 사용하는 것이 편리하다.
    • re.compile('\b가족\b') : \b를 escape 문자 b(백스페이스)로 인식
    • re.compile(r'\b가족\b') : \b가 일반문자가 되어 컴파일시 정규식 메타문자로 처리된다.

📍 검색함수

  • match(), search() : 패턴과 일치하는 문장이 있는지 여부를 확인할 때 사용
  • findall() : 패턴과 일치하는 문장을 찾을 때 사용

🌓  Match 객체

  • 검색 결과를 담아 반환되는 객체
    • match(), search() 의 반환타입
  • 패턴과 일치한 문자열과 대상문자열 내에서의 위치를 가지고 있는 객체
  • 주요 메소드
    • group() : 매치된 문자열들을 튜플로 반환
    • group(subgroup 번호) : 패턴에 하위그룹이 지정된 경우 특정 그룹의 문자열 반환
    • start(), end() : 대상 문자열내에서 시작, 끝 index 반환
    • span() : 대상 문자열 내에서 시작, 끝 index를 tuple로 반환

🌓  match(대상문자열 [, pos=0])

  • 대상 문자열의 시작 부터 정규식과 일치하는 것이 있는지 조회
  • pos : 시작 index 지정
  • 반환값
    • Match 객체: 일치하는 문자열이 있는 경우
    • None: 일치하는 문자열이 없는 경우
import re

# txt = "안녕하세요. 저 나이는 20세 입니다."
txt = "반갑습니다. 안녕하세요. 저 나이는 20세 입니다."
pattern = r'안녕' #literal 로만 만든 패턴
# 함수
m = re.match(pattern, txt) #패턴, 찾을 대상 -> 문장의 시작이 pattern과 일치하는지.
print(m)
if m:
    print('찾은 문자열:', m.group())
    print('찾은 문자열의 시작/끝 위치, 전체위치:', m.start(),m.end(),m.span())
    
else: #m == None
    print('없음')
>>>
None
없음

🌓  search(대상문자열 [, pos=0])

  • 대상문자열 전체 안에서 정규식과 일치하는 것이 있는지 조회
  • pos: 찾기 시작하는 index 지정
  • 반환값
    • Match 객체: 일치하는 문자열이 있는 경우
    • None: 일치하는 문자열이 없는 경우
txt = "반갑습니다. 안녕하세요. 저 나이는 20세 입니다.안녕하세요."
pattern = r'안녕하세요'
p = re.compile(pattern)
m = p.search(txt,pos=9) # 뒤에 안녕하세요가 있어도 앞에 있으면 그것만 검색한다. / but pos=9 로 하면 '9'이후의 값부터 검색함.
if m:
    print(m.group(), m.span())
else:
    print('없음')
>>>
안녕하세요 (28, 33)

🌓  findall(대상문자열)

  • 대상문자열에서 정규식과 매칭되는 문자열들을 리스트로 반환
  • 반환값
    • 리스트(List) : 일치하는 문자열들을 가진 리스트를 반환
    • 일치하는 문자열이 없는 경우 빈 리스트 반환
txt = '가격은 각각 4000, 5000, 15000, 25000, 10, 9, 236입니다.'
# pattern = r'\\d' # 숫자 1개
pattern = r'\\d+' # 숫자 1개 이상이의 숫자들
pattern = r'[가-힣]+' # 1개 이상의 한글들
p = re.compile(pattern) 
m = p.findall(txt)
print(m)
>>>
['가격은', '각각', '입니다']

🌓  finditer(대상문자열)

  • 패턴에 일치하는 모든 문자열을 찾아주는 Iterator => for문, list()
  • 찾은 문자열을 Match 객체로 반환.
result = p.finditer(info)
print(type(result))
for m in result:
    print(m)

📍 문자열 변경

  • sub( ): 변경된 문자열 반환
  • subn( ): 변경된 문자열, 변경개수 반환

🌓  sub(바꿀문자열, 대상문자열 [, count=양수])

  • 대상문자열에서 패턴과 일치하는 것을 바꿀문자열로 변경한다.
  • count: 변경할 개수를 지정. 기본: 매칭되는 문자열은 다 변경
  • 반환값: 변경된 문자열
txt = 'test1, test2, test3, test4, test5'
pattern = r'\\D' # or r'[^0-9]'
p = re.compile(pattern)
m = p.sub('', txt) # 지우기 => '': 빈문자열 (0글자)로 변경
print(m)
>>>
12345

🌓  subn(바꿀문자열, 대상문자열 [, count=양수])

  • sub()와 동일한 역할.
  • 반환값 : (변경된 문자열, 변경된문자열개수) 를 tuple로 반환
#subn 의 n은 문자열의 갯수
txt = '오늘은     수요일     입니다.'
txt = """오늘은    수요일     입니다.
내일은       목요일      입니다.
내일은     금요일    입니다
"""
# txt = '오늘은  수요일   입니다.'
# 공백들을 공백한개로 변경.
#pattern = r' +' #공백들
pattern = r'\\s+'  #\\s : 공백, tab, 엔터
p = re.compile(pattern)
# result = p.sub(' ', txt)cn

result, cnt = p.subn(' ', txt)
print('변경개수:', cnt)
print(result)
>>>
변경개수: 9
오늘은 수요일 입니다. 내일은 목요일 입니다. 내일은 금요일 입니다

📍 나누기(토큰화)

🌓  split(대상문자열)

  • pattern을 구분자로 문장을 나눈다.
  • 반환: 나눈 문자열을 원소로 하는 리스트
'사과,복숭아,배,수박'.split(',')
>>>
['사과', '복숭아', '배', '수박']
-------------
# txt = '사과,복숭아,배|수박'
p = re.compile(r'[,|]') # 구분자 패턴
p.split(txt)
>>>
['사과', '복숭아', '배', '수박']

4. 그룹핑(Grouping)

  • 패턴 내에서 하위패턴을 만드는 것.
    • 전체 패턴에서 일부 패턴을 묶어준다.
  • 구문: (패턴)

📍 그룹핑 예

🌓  전체 패턴 내에서 일부 패턴을 조회

tel = "TEL: 010-1111-2222"
pattern = r'(\\d{2,3})-(\\d{3,4})-(\\d{4})'
p = re.compile(pattern)
m = p.search(tel)
print(m)
if m:
    print(m.group()) # 패턴과 일치하는 전체 문장을 반환.
    print(m.group(1)) # 첫번쨰() 로 묶인 하위그룹 패턴을 반환.
    print(m.group(2))
    print(m.group(3))
>>>
<re.Match object; span=(5, 18), match='010-1111-2222'>
010-1111-2222
010
1111
2222
----------------
m.group().split('-')[2]
>>>
'2222'

🌓  패턴 내에서 하위그룹 참조

  • 지정한 “번호” 번째 패턴으로 매칭된 문자열과 같은 문자열을 의미
txt = """
010-1111-2222
010-2222-2222
010-3333-4444
010-5555-5555
"""

# 패턴: 국번과 번호가 같은 전화번호
pattern = r'\\d{2,3}-(\\d{3,4})-\\1'  #\\1: 1번 하위그룹으로 찾은 값과 같은 값을 가진 것.(패턴뿐 아니라 값도 같아야 한다.)
p = re.compile(pattern)            #(\\1): \\1도 그룹으로 만들 수 있다. 그럼 m.group(2) = (\\1) 이다.
for m in p.finditer(txt):
    print(m.group())
>>>
010-2222-2222
010-5555-5555

🌓  패턴내의 특정 부분만 변경

print(info)
# 주민번호중에 마지막 6개 숫자를 #으로 변경
pattern = r'(\\d{6}-[012349])\\d{6}'   # 찾은 것 중 남길것(변경안할 것)을 그룹으로 묶는다.
p = re.compile(pattern)
result = p.sub("\\g<1>######",info) #\\g<1>: 1번 그룹의 값으로 변경
print(result)
>>>
김정수 kjs@gmail.com 801023-1010221
박영수 pys.abc@gmail.com 700121-1120212
이민영 lmy-abc@naver.com 820301-2020122
김순희 ksh@daum.net 781223-2012212
오주연 ojy@daum.net 900522-1023218

김정수 kjs@gmail.com 801023-1######
박영수 pys.abc@gmail.com 700121-1######
이민영 lmy-abc@naver.com 820301-2######
김순희 ksh@daum.net 781223-2######
오주연 ojy@daum.net 900522-1######

group으로 묶인 것 참조(조회)

  • 패턴 안에서 참조
    • \\번호 , r'(\\d{3}) \\1' => 중복되는 것을 패턴으로 표현할 때.
  • match 조회
    • match객체.group(번호)
  • sub() 함수에서 대체 문자로 참조
    • \\g<번호>

5. Greedy 와 Non-Greedy

📍 Greedy(탐욕스러운-최대일치) 의 의미

  • 주어진 패턴에 만족하는 문자열을 최대한 넓게(길게) 잡아 찾는다.
  • 매칭시 기본 방식

📍 Non-Greedy(최소일치)

  • 주어진 패턴에 만족하는 문자열을 최초의 일치하는 위치까지 찾는다
  • 개수를 나타내는 메타문자(수량자)에 **?**를 붙인다.
  • ?, +?, {m,n}?
txt = '<div>파이썬 <b>정규표현식</b> </div>'
pattern = r'<.+?>'
p = re.compile(pattern)
m = p.findall(txt)
print(m)
>>>
['<div>', '<b>', '</b>', '</div>']
txt = '<div>파이썬 <b>정규표현식</b> </div>'
pattern = r'<.+?>'
p = re.compile(pattern)
m = p.findall(txt)
print(m)
>>>
['<div>', '<b>', '</b>', '</div>']

6. 전방/후방 탐색

  • 패턴과 일치하는 문자열을 찾을 때는 사용하되 반환(소비) 되지 않도록 하는 패턴이 있을 때 사용.

📍 전방탐색

  • 반환(소비)될 문자열들이 앞에 있는 경우.
  • 긍정 전방탐색
    • %%%(?=패턴) : %%%-반환될 패턴
  • 부정 전방탐색
    • %%%(?!패턴) : 부정은 =를 !로 바꾼다.
info = """
TV 300001원 30개
컴퓨터 2300001원 50개
모니터 42000001원 70개
"""
# 가격만 조회
# pattern = r'\\d+원'
# 가격만 조회 -> 조회결과에서 '원'은 뺸다.'
pattern = r'\\d+(?=원)'
p = re.compile(pattern)
m = p.findall(info)
print(m)
>>>
['300001', '2300001', '42000001']

📍 후방탐색

  • 반환(소비)될 문자열이 뒤에 있는 경우.
  • 긍정 후방탐색
    • (?<=패턴)%%%
  • 부정 후방탐색
    • (?<!패턴)%%%
반응형

'[T.I.L] : Today I Learned > Python' 카테고리의 다른 글

[Python] 10강_Iterable 과 Decorator  (0) 2023.04.09
[Python] 9-2강_입출력  (2) 2023.04.09
[Python] 9-1강_Path  (0) 2023.04.09
[Python] 8강_예외처리  (0) 2023.04.09
[Python] 7강_패키지 모듈 import  (0) 2023.04.05