-
re (regex - regular expression)파이썬 Study/라이브러리 2021. 3. 13. 11:56
출처:
https://bradbury.tistory.com/47
https://whatisthenext.tistory.com/116
■ re는 사용이 어렵고... 정리 하여도 잘 까먹으므로
- search 기능 / match 기능을 어느정도 잘 쓸 수 있게 하자!
- escapeㅁ 문자 \ 이용하기 위해 , 반드시 text 열 앞에 r로 raw text로 만드는 것 중요
> ex : r'c{1, }'
■ Search
1) 정의 :
문자열에서 특정 표현( ~= 패턴 ) 찾기
- f.y.i] str의 find( ) method도 존재 : 찾는 문자가 문자열 안에 없으면 -1, 있으면 위치 index 값 리턴
■ Search - 축약형
- 한줄로 코드 절약 가능
import re hand = 'This is a message From: your friend!' if re.search('From:',hand): # this is not None -> if specific character exsists print(re.search('From:',hand)) >>> <re.Match object; span=(18, 23), match='From:'> print(f'hand[18:23] : {hand[18:23]}') >>> hand[18:23] : From:
source = 'Lux, the Lady of Luminosity' m = re.match('[a-z]+', source )
■ Search - 컴파일 후 매칭
- 객채 object 생성 후 여러번 재사용 가능
- 반복 패턴의 경우 미리 컴파일을 하여 빠른 사용 가능
import re # re 내장모듈 내(.) compile 메서드를 사용. # compile 메서드는 "패턴 객체"를 반환한다. p = re.compile('[a-z]+') # 패턴 객체(p)에는 또다시 검색 메서드가 있다. m = p.match("python")
■ 메타문자 - 패턴생성
. ^ $ + ? { } [ ] \ | ( )
패턴 표
-----------------------------------------------------------------
^
&
라인의 처음을 매칭(문장의 시작)
-----------------------------------------------------------------
$
라인의 끝을 매칭(문장의 끝)
-----------------------------------------------------------------
.
\n 제외 임의의 문자를 매칭 (와일드 카드)
점 하나는 글자 하나를 의미
-----------------------------------------------------------------
\s
공백 문자를 매칭(\n, \t, \b)
-----------------------------------------------------------------
\S
공백이 아닌 문자를 매칭
-----------------------------------------------------------------
\d
숫자(1digit) === [0-9]
-----------------------------------------------------------------
\D
특수문자(!,%,$...) + 텍스트 + 화이트스페이스
-----------------------------------------------------------------
*
바로 앞선 문자에 적용되고 0 혹은 그 이상의 앞선 문자와 매칭을 표기함
(앞의 문자가 여러번 반복 될 수 있다)
-----------------------------------------------------------------
{m, n}
m회 이상 n회 이하
-----------------------------------------------------------------
*?
바로 앞선 문자에 적용되고 0 혹은 그 이상의 앞선 문자와 매칭을
탐욕적이지 않은 방식으로 표기
-----------------------------------------------------------------
+
바로 앞선 문자에 적용되고 1 혹은 그 이상의 앞선 문자와 매칭을 표기함
(앞의 문자가 1번 이상 나타난다)
-----------------------------------------------------------------
?
0회 이상 1회 이하
-----------------------------------------------------------------
+?
바로 앞선 문자에 적용되고 1 혹은 그 이상의 앞선 문자와 매칭을
탐욕적이지 않은 방식으로 표기함
-----------------------------------------------------------------
\
이스케이프, 또는 메타 문자를 일반 문자로 인식하게 하는 시작 기호
-----------------------------------------------------------------
[ ]
문자 클래스
-----------------------------------------------------------------
[aeiou]
명시된 집합 문자에 존재하는 단일 문자와 매칭.
ex) 위는 “a”, “e”, “i”, “o”, “u” 문자만 매칭되는 예제
-----------------------------------------------------------------
[a-z0-9]
- 기호로 문자 범위를 명세할 수 있다.
소문자이거나 숫자인 단일 문자만 매칭되는 예제
-----------------------------------------------------------------
( )
괄호가 정규표현식에 추가될 때, 매칭을 무시한다.
하지만 findall()을 사용 할 때 전체 문자열보다 매칭된 문자열의
상세한 부속 문자열을 추출할 수 있게 한다.
→ 그룹핑, 추출할 패턴을 지정한다
-----------------------------------------------------------------
|
or 조건식 줄 때 사용
■ 컴파일 후 사용법
1) [ ] 대괄호 사용법
- 문자 "한 개" 와 매칭된다
(a) 대괄호 안에 아무 문자클래스를 넣을 수 있음
엄밀하게 구분이 된다
( ex : a / A, z / Z ) 가 다름 : 소문자 ↔ 대문자 는 서로 같은 문자가 아니다 (다른 문자)
(b) 안에 들어간 것들 끼리 - 하이픈으로 연결 가능
( ex : [a-A] == \S, [0-9] == \d ) : 축약 표현은 문자클래스 지정 안함(바로 쓴다)
(c) ^ 개행은 문자 클래스 [ ] 에서는 not .... BUT .... 문자열에서는 처음과 일치를 의미
(d) 사이에 메타 문자 끼워넣기 : [.], [-] 등은 실제 문자 자체를 의미 . 과 -, 즉 메타문자 → 실제 문자
a[a-zA-Z0-9]z → aBz, a9z 등과 매칭
[0-9] \d 숫자
[^0-9] \D 텍스트 + 특수문자 + 화이트스페이스
[a-zA-Z0-9] \w 텍스트 + 숫자( 언더스코어 _ 포함 )
[^a-zA-Z0-9] \W 특수문자 + 공백
[ \t\n\r\f\v] \s whitespace 문자
[^ \t\n\r\f\v] \S whitespace 아닌 것 = 텍스트 + 특수문자 + 숫자
2) Dot( . ) 사용법
- 문자 "한 개" 와 매칭된다(그 어떤 문자이던 \d + \D == \w + \s)
(a) 도트 두개는 문자 두개와 매칭된다
(b) 도트 문자 포함의 범위는 숫자, 문자, 특수문자 모드를 포함
a.z → axz, a9z, a!z 등과 매칭
3) 반복 ( * ) 사용법
- 바로 앞 문자 / 문자 클래스 가 "0 번" 이상 반복
(a) 문자가 없을 수도 있을 때 사용
.* → 하나 이상의 문자를 포함하는 문자열
ab*c → ac, a123c, abbbbclike.* → like, likely, .... like로 시작하는 그 어떤 문자열과 매치
4) 반복 ( + ) 사용법
- 바로 앞 문자 / 문자 클래스 가 "1 번" 이상 반복
ca+t → cat, caaat, caaaat
[A-Z]+ → 대문자로만 이루어진 문자열 ABC, DEF ...
import re source = "Luke Skywarker 02-123-4567 luke@daum.net" # \w와 \w+의 차이 m1 = re.findall('\w', source) # 단어가 아니라 문자 단위로 출력 m2 = re.findall('\w+', source) # 단어 단위로 출력, greedy하게 최대 출력 print("m1 : ", m1) print("m2 : ", m2) >>> 출력결과 m1 : ['L', 'u', 'k', 'e', 'S', 'k', 'y', 'w', 'a', 'r', 'k', 'e', 'r', '0', '2', '1', '2', '3', '4', '5', '6', '7', 'l', 'u', 'k', 'e', 'd', 'a', 'u', 'm', 'n', 'e', 't'] m2 : ['Luke', 'Skywarker', '02', '123', '4567', 'luke', 'daum', 'net']
5) 반복 { } 사용법
- 바로 앞 문자 / 문자 클래스 반복횟수 지정
ca{2}t a가 2회 반복되어야 함 caat ca{2,5}t a가 2회 이상 5회 이하 반복되어야 함 caat, caaat, caaaat ca{0, }t 반복횟수 0회 이상 (*와 동일) ct, cat, caat, caaat, caaaat cat{0, 1}t 반복횟수 0회 ~ 1회 이하 (?와 동일) ct, cat cat{ , 3} 반복횟수 0회 이상 ~ 3회 이하 ct, cat, caat, caat
6) 반복 ? 사용법
- 바로 앞 문자 0회 / 1회 둘 중 하나 등장과 매치
ab?c b가 있어도 되고 없어도 된다. ac, abc
7) string 안의 반복문 찾기
- r'(.)\1{1, }'
- r 을 써주어야 \를 글자로 인식 (raw text화)
- ( )으로 . 과 매치되는 single 문자열 찾고 그룹을 부여
- \1 로 ( ) 첫 그룹에서 찾은 문자를 아직 매칭을 찾지 않은 남은 문자열에서 한번 더 찾음(이 시점에서 2개 반복되는 char)
- { } 로 바로 앞 글자 1회 이상 반복으로 2회 이상 반복 char를 찾음
import re stringList = ['112342221234441', 'aabbbcdaeegh'] # group 서치 했기 때문에 find iter로 group 별로 뽑아야 함 reObj = re.compile(r'(.)\1{1,}') for string in stringList: reIter = reObj.finditer(string) for obj in reIter: print(obj) print(obj.group()) print('\n') reSearch = reObj.search(string) print(reSearch.group()) print('\n') for findrtn in reObj.findall(string): print(findrtn) print('-='*3) print('\n'*2) >>> # re의 iter 매써드만 잘 나옴 <re.Match object; span=(0, 2), match='11'> 11 <re.Match object; span=(5, 8), match='222'> 222 <re.Match object; span=(11, 14), match='444'> 444 <re.Match object; span=(0, 2), match='aa'> aa <re.Match object; span=(2, 5), match='bbb'> bbb <re.Match object; span=(8, 10), match='ee'> ee
8) [부정/긍정] [전방/후방] 탐색
(a) 긍정 전방탐색 (?=<regex>)
(b) 긍정 후방탐색 (?<=<regex>)
(c) 부정 전방탐색 (?!<regex>)
(d) 부정 후방탐색 (?<!<regex>)
[ = ] : 긍정 / [ ! ] : 부정
[ 공백 ] : 전방 / [ < ] : 후방▶ ( ) 로 감싸서 group 화 시킴
▶ <p> 태그 안의 모든 텍스트를 검사
▶ <p> 태그는 포함하지만 결과에는 제외하고 싶음, <p>는 정규식의 일치부에 넣지 않도록 함
▶ 후방탐색과 전방탐색을 문자 덩어리 앞뒤에 쓰면
<p> 태그 안이라는 조건을 설정하면서 동시에 <p> 태그는 일치부에서 제외시킬 수 있음
> 예시를 통한 이해
print(re.search('(?<=<p>)\w+(?=</p>)', 'Kakao <p>ryan</p> keep a straight face.').group()) >>> ryan
→ 후방까지 <p>를 찾고 전방에는 </p>로 끝나는 패턴에 그리디한 \w + 로 태그 안을 검색,
감싸는 <p></p>는 제외
p = re.compile(".+(?=:)") m = p.search("http://google.com") print(m.group()) >>> http
# foo.bar, autoexec.bat, sendmail.cf 같은 형식의 파일 import re reObj = re.compile(".*[.](?!bat$|exe$).*$") # all_letter + dot + (no ending with bat | exe) +(조건:ends in one line) print(reObj.findall("foo.bar")) >>> ['foo.bar'] print(reObj.findall("autoexec.bat")) >>> [] print(reObj.findall("autoexec.bat_")) >>> ['autoexec.bat_'] print(reObj.findall("sendmail.cf")) >>> ['sendmail.cf']
9) Greedy VS Non-Greedy
(a) Greedy
- 해당 문자 : *, + , { }
- 문자는 greedy, 최대한 매칭하게 찾기 때문에 최대일치 패턴을 찾음
import re source = <li>나이키</li><li>아디다스</li><li>퓨마</li> m = re.match('<li>.*</li>', source) if m: print(m10.group()) >>> <li>나이키</li><li>아디다스</li><li>퓨마</li>
(b) Non-greedy
- (a) 의 greedy 문자 바로 우측에 ? 문자를 붙여주면 최소일치 패턴으로 바뀜
# non-greedy print(re.match('<.*?>', s).group()) >>> <html>
10) ★위의 메타 문자들의 특성★
(a) 소비형 메타 문자
> +, *, ?, { } 는 문자열 소모(소비)하는 방식, 반복 조건을 마치면 검색에서 제외
(=== 문자열의 위치가 변하면서 검색)
(b) 비소비형 메타 문자
> |, ^, $, \, ( ) 는 문자열의 위치와 관련 없는 절대 속성을 패턴으로 삼는다
- | : or
p = re.compile('a|b') # a 또는 b를 찾는다. m = p.findall("abcdefg") print(m) >>> ['a', 'b']
- ^, & : 맨 처음 일치
m1 = re.findall("^Life", "Life is too short") m2 = re.findall("^is", "Life is too short") print("m1 결과 : ", m1) >>> m1 결과 : ['Life'] print("m2 결과 : ", m2) >>> m2 결과 : []
- $ : 맨 마지막 일치
m1 = re.findall("short$", "Life is too short") m2 = re.findall("short$", "Life is too short. So what?") print("m1 결과 : ", m1) >>> m18 결과 : ['short'] # 문장 끝이 short기 때문에 short를 반환한다. print("m2 결과 : ", m2) >>> m19 결과 : [] # 문장 끝이 short가 아니기 때문에 반환값이 없다.
11) ( ) Grouping
- 검색 결과의 특정 부분만을 출력
- ( ) 감싼 부분으로 검색 index 이 되는것이라고 이해
> group method 가능한 Match매써드
match, search : re.Match 객체를 돌려주므로 group 매써드 사용 가능한 애들
<re.Match>.group( ) : re.Match 안의 패턴 검색된 것 모두 return 됨
import re p = re.compile("(\w+)\s+(\d+[-]\d+[-]\d+)") m = p.search("park 010-1234-1234") print(m) >>> <re.Match object; span=(0, 18), match='park 010-1234-1234'> print(f'type(m):{type(m)}') >>> type(m):<class 're.Match'> print(f'm.group:{m.group}') >>> m.group:<built-in method group of re.Match object at 0x7efc891eadb0> print(f'm.group() : {m.group()}') >>> m.group() : park 010-1234-1234 print(m.group(0)) >>> park 010-1234-1234 print(m.group(1)) >>> park print(m.group(2)) >>> 010-1234-1234
12) re.Match 객체의 매써드
- match / search 매써드로 수행 된 결과값인 re.Match 객체에 대한 내용
(a) group( ) : 매치 된 문자열을 돌려준다
(b) start( ) : 매치 된 문자열의 시작 위치를 돌려준다
(c) end( ) : 매치 된 문자열의 끝 위치를 돌려준다
(d) span( ) : 매치된 문자열의 (시작, 끝) 의 튜플을 돌려준다
13) 알면 좋은 compile option
> VERBOSE, X
- 정규식을 주석 / 줄단위로 구분하여 compile 하기 위한 옵션
(a) 원본
import re reg_1 = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
# VERBOSE / X form reg_1 = 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) # re.X
VERBOSE / X form
- 훨씬 가독성이 좋음
- 주석을 적고 여러 줄로 표현하는 것이 가능, 줄 단위로 #기호를 사용하여 주석문을 작성
- re.VERBOSE 옵션을 사용하면 문자열에 사용된 whitespace는 컴파일할 때 제거
(단 [ ] 안에 사용한 whitespace는 제외)
■ 실제 compile 된 패턴의 검색법
1-1) match : 문자열의 처음부터 compile 된 정규식과 매치되는지 조사(fail 시 None return)
→ 반드시 '첫' 케이스가 중요할 때 사용
1-2) search : 문자열 전체를 compile 된 정규식과 매치되는지 조사
> 발견 되면 그 지점에서 search / match를 멈춘다
> 존재시re.Match 객체가 return 된다
# match와 search import re source13 = ''' All That Is Gold Does Not Glitter ''' match = re.match("Not", source13) search = re.search("Not", source13) # re.Match 객체가 없는 경우 None if match: print("match : ", match.group()) # group 매써드로 모두 출력 >>> None if search: print("search : ", search.group()) # group 매써드로 모두 출력 >>> search : Not
2-1) findall : 정규식과 매치되는 모든 문자열을 list로 반환
2-2) finditer : 정규식과 매치되는 모든 문자열을 iterator로 반환 - for문으로 next( ) 매써드 사용하여 값을 출력하거나 형변환
→ findall 을 주로 쓰는게 편함
import re p = re.compile('[a-z]+') # 소문자(a-z)가 1회 이상 반복되는 걸 찾아와라. m = p.findall("Life is to short") print(type(m)) >>> <class 'list'> print(m) >>> ['ife', 'is', 'to', 'short']
3) split : compile 된 정규식 일치하는 부분을 나누기
obj = re.compile( <pattern to search> )
obj.split(<string to find pattern>, [N] ) # N : number of times to perform splitimport re reObj = re.compile('<[^<>]*>') print(reObj.split( '<html> Wow <head> header </head> <body> Hey </body> </html>', 2)) >>> ['', ' Wow ', ' header </head> <body> Hey </body> </html>']
4) sub : compile 된 정규식 일치하는 부분 대체
obj = re.compile( <pattern to search> )
obj.sub( <string to replace with>, <string to find pattern>, [count = N] ) # count : number of times to replaceimport re p = re.compile('(blue|white|red)') p.sub('colour', 'blue socks and red shoes') >>> 'colour socks and colour shoes' p.sub('colour', 'blue socks and red shoes', count=1) >>> 'colour socks and red shoes'
반응형'파이썬 Study > 라이브러리' 카테고리의 다른 글
collections - deque (1) 2021.03.14 String 문자열 관련 methods (1) 2021.03.14 hashing (1) 2021.03.11 collections - defaultdict (1) 2021.03.11 queue (1) 2021.03.11