정규표현식(regular expression)
- 일정한 규칙(패턴)을 가진 문자열을 표현하는 방법
- 복잡한 문자열 속에서 특정한 규칙으로 된 문자열을 검색,추출,수정 할때 사용
- 문자열이 정해진 규칙에 맞는지 판단할 때 사용
- re모듈을 가져와서 사용 (regular expression)
re모듈에 속한 함수
- match() / search() / findall() / finditer() 등등
문자열 판단하기
- re.match('패턴', '문자열')
import re
re.match('Hello', 'Hello, world!')
#결과
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
re.match('Python', 'Hello, world!')
#매치된 문자열이 없어 아무것도 반환 하지 않음
문자열이 맨 앞에 오는지 맨 뒤에 오는지 판단하기 (search함수 사용)
- ^문자열 : 문자열이 앞에 오는 지 판단하기
- 문자열$ : 문자열이 뒤에 오는지 판단하기
- re.search('패턴' , '문자열') :패턴에 위의 2가지 패턴을 사용
re.search('^Hello', 'Hello, world!')
#결과
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
re.search('world!$', 'Hello, world!')
#결과
<_sre.SRE_Match object; span=(7, 13), match='world!'>
지정된 문자열이 하나라도 포함되는지 판단하기
- 문자열 | 문자열
- 문자열 | 문자열 | 문자열 | 문자열
re.match('hello|world', 'hello') # hello 또는 world가 있으므로 패턴에 매칭됨
#결과
<_sre.SRE_Match object; span=(0, 5), match='hello'>
범위 판단하기
- [범위]* : 범위에 해당하는 문자가 0개이상 있는지
- [범위]+ : 범위에 해당하는 문자가 1개이상 있는지
- 범위 종류
- 숫자 : [0-9]
- 소문자 : a-z
- 대문자 : A-Z
- 한글 : 가-힣
re.match('[0-9]*', '1234') # 1234는 0부터 9까지 숫자가 0개 이상 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 4), match='1234'>
re.match('[0-9]+', '1234') # 1234는 0부터 9까지 숫자가 1개 이상 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 4), match='1234'>
re.match('[0-9]+', 'abcd')
#매칭되는게 없으므로 반한되는 값이 없음.
[범위]* 는 해당하는것이 없어도 반환값이 나온다 그렇다면 왜 사용하는 것일까?
re.match('a*b', 'b') # b에는 a가 0개 이상 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 1), match='b'>
re.match('a+b', 'b')
# 해당 값이 없어 반환되는 값이 없음
re.match('a*b', 'aab') # aab에는 a가 0개 이상 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 3), match='aab'>
re.match('a+b', 'aab') # aab에는 a가 1개 이상 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 3), match='aab'>
re.match('a+b', 'aaa')
#b가 없으므로 반한되지않음.
문자가 한 개만 있는지 판단하기
- 문자? : 해당 위치에 문자가 0개 or 1개 인지 판단 (공백은 안됌)
- [0-9]? : 해당 위치에 숫자가 0개 or 1개 인지 판단 (공백은 안됌)
- . : 해당위치에 아무 문자가 1개 있는지 판단 (아무문자에 공백도 포함)
- 문자열 길이가 모자른다면 반환값이 없음
re.match('abc?d', 'abd') # abd에서 c 위치에 c가 0개 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 3), match='abd'>
re.match('ab[0-9]?c', 'ab3c') # [0-9] 위치에 숫자가 1개 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 4), match='ab3c'>
re.match('ab.d', 'abxd') # .이 있는 위치에 문자가 1개 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 4), match='abxd'>
문자 개수 판단하기
- 문자{개수}
- (문자열){개수}
re.match('h{3}', 'hhhello')
<_sre.SRE_Match object; span=(0, 3), match='hhh'>
re.match('(hello){3}', 'hellohellohelloworld')
<_sre.SRE_Match object; span=(0, 15), match='hellohellohello'>
[0-9]{개수} : 숫자의 개수를 판단하는 문구로 핸드폰 번호 형식 판단.
re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-1000')
<_sre.SRE_Match object; span=(0, 13), match='010-1000-1000'>
re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-100')
#매칭되지 않음
개수의 범위 지정하기
- (문자){시작개수 , 끝개수}
- (문자열){시작개수 , 끝개수}
- [0-9]{시작개수 , 끝개수}
re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-100-1000')
<_sre.SRE_Match object; span=(0, 11), match='02-100-1000'>
re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-10-1000')
# 반환값 없음
숫자와 영문 문자를 조합해서 판단하기
re.match('[a-zA-Z0-9]+', 'Hello1234')
<_sre.SRE_Match object; span=(0, 9), match='Hello1234'>
re.match('[A-Z0-9]+', 'hello')
# 반환값 없음
특정 문자 범위에 포함되지 않는지 판단하기
- [^범위]*
- [^범위]+
re.match('[^A-Z]+', 'Hello')
# 대문자를 제외. 대문자가 있으므로 패턴에 매칭되지 않음
re.match('[^A-Z]+', 'hello') # 대문자를 제외. 대문자가 없으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='hello'>
특수 문자 판단하기 (search함수 사용)
- \특수문자
re.search('\*+', '1 ** 2') # *이 들어있는지 판단
<_sre.SRE_Match object; span=(2, 4), match='**'>
re.match('[$()a-zA-Z0-9]+', '$(document)') # $, (, )와 문자, 숫자가 들어있는지 판단
<_sre.SRE_Match object; span=(0, 11), match='$(document)'>
\를 사용한 간단한 처리 방식
- \d: [0-9]와 같음. 모든 숫자
- \D: [^0-9]와 같음. 숫자를 제외한 모든 문자
- \w: [a-zA-Z0-9_]와 같음. 영문 대소문자, 숫자, 밑줄 문자
- \W: [^a-zA-Z0-9_]와 같음. 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자
공백 처리하기
공백의 종류
- ' ' 일반 공백(스페이스)
- \t(탭)
- \n(새 줄, 라인 피드)
- \r(캐리지 리턴)
- \f(폼피드)
- \v(수직 탭)
공백 처리 방식
- \s: [ \t\n\r\f\v]와 같음. 공백(스페이스)
- \S: [^ \t\n\r\f\v]와 같음. 공백을 제외하고 \t, \n, \r, \f, \v만 포함
re.match('[a-zA-Z0-9 ]+', 'Hello 1234') # ' '로 공백 표현
<_sre.SRE_Match object; span=(0, 10), match='Hello 1234'>
re.match('[a-zA-Z0-9\s]+', 'Hello 1234') # \s로 공백 표현
<_sre.SRE_Match object; span=(0, 10), match='Hello 1234'>
re.match('[a-zA-Z0-9\S]+', 'Hello 1234') *\S는 공백을 포함하지않음
<re.Match object; span=(0, 5), match='Hello'>
같은 정규표현식 패턴을 자주 사용할때
compile 함수를 사용하여 정규표현식 패턴을 객체로 만든 뒤 match() or search() 사용
객체 = re.compile('패턴')
객체.match('문자열')
#or
객체.search('문자열')
그룹 사용하기
- (정규표현식) (정규표현식). *사이에 공백 꼭 해줘야함
- 매치객체.group(그룹숫자)
m = re.match('([0-9]+) ([0-9]+)', '10 295')
m.group(1) # 첫 번째 그룹(그룹 1)에 매칭된 문자열을 반환
'10'
m.group(2) # 두 번째 그룹(그룹 2)에 매칭된 문자열을 반환
'295'
m.group() # 매칭된 문자열을 한꺼번에 반환
'10 295'
m.group(0) # 매칭된 문자열을 한꺼번에 반환
'10 295'
(자문) 그룹1 과 그룹2의 표현식은 같은데 왜 다른 값을 반환 할까..?
(자문) 왜 띄어쓰기를 하지않으면 반환값이 1 일까?
- 매치객체.groups() : 그룹에 해당하는 문자열을 튜플로 반환
m.groups()
('10', '295')
그룹에 네이밍해서 사용하기
- (?P<이름>정규표현식)
- 매치객체.group('그룹이름')
m = re.match('(?P<func>[a-zA-Z_][a-zA-Z0-9_]+)\((?P<arg>\w+)\)', 'print(1234)')
#func과 arg로 네이밍
m.group('func')
'print'
m.group('arg')
'1234'
- 함수 ( , ) 는 정규표현식에 사용하는 특수 문자이므로 앞에 \를 붙여준다 (자문) ??? 뭔말??
findall() - 패턴에 매칭되는 모든 문자열 가져오기
- re.findall('패턴' , '문자열')
re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8')
['1', '2', '4', '7', '8']
* , + 와 그룹 활용하기
(.[a-z]+)* 은 점과 영문 소문자가 1개 이상 있는지 판단하고
이것 자체가 0개 이상인지 판단 하여야한다.
*규칙은 반드시 지켜야 하지만 있어도 되고 없어도 되는 상황
re.match('[a-z]+(.[a-z]+)*$', 'hello.world') # .world는 문자열이므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 11), match='hello.world'>
re.match('[a-z]+(.[a-z]+)*$', 'hello.1234')
# .1234는 숫자이므로 패턴에 매칭되지 않음
re.match('[a-z]+(.[a-z]+)*$', 'hello') # .뒤에 문자열이 없어도 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='hello'>
존재하지않아도 되지만 존재한다면 조건에 충족해야 한다.
sub() - 문자열 바꾸기
- re.sub('패턴' , '바꿀문자열' , '문자열' , 바꿀횟수) : 횟수 생략 가능
re.sub('apple|orange', 'fruit', 'apple box orange tree')
# apple 또는 orange를 fruit로 바꿈
'fruit box fruit tree'
바꿀 문자열에 교체 함수를 지정 가능
- 교체함수(매치객체)
- re.sub('패턴' , 교체함수 , '문자열' , 바꿀횟수)
#함수 만들기
def multiple10(m): # 매개변수로 매치 객체를 받음
n = int(m.group()) # 매칭된 문자열을 가져와서 정수로 변환
return str(n * 10) # 숫자에 10을 곱한 뒤 문자열로 변환해서 반환
#sub 사용
re.sub('[0-9]+', multiple10, '1 2 Fizz 4 Buzz Fizz 7 8')
#결과
'10 20 Fizz 40 Buzz Fizz 70 80'
람다 식 표현으로 간단하게 사용해 보기
re.sub('[0-9]+', lambda m: str(int(m.group()) * 10), '1 2 Fizz 4 Buzz Fizz 7 8')
'10 20 Fizz 40 Buzz Fizz 70 80'
찾은 문자열을 결과에 다시 사용하기
- re.sub(('정규표현식') ('정규표현식') , '\\숫자 \\숫자 \\숫자' , '문자열')
\\숫자는 그룹의 번호 이고 해당 숫자의 그룹에 매칭된 문자열을 다시 가져와 사용
re.sub('([a-z]+) ([0-9]+)', '\\2 \\1 \\2 \\1', 'hello 1234')
'1234 hello 1234 hello'
응용 하기
re.sub('({\s*)"(\w+)":\s*"(\w+)"(\s*})', '<\\2>\\3</\\2>', '{ "name": "james" }')
'<name>james</name>'
그룹 이름을 지었다면?
re.sub('({\s*)"(?P<key>\w+)":\s*"(?P<value>\w+)"(\s*})',
'<\\g<key>>\\g<value></\\g<key>>', '{ "name": "james" }')
'<name>james</name>'
아주 어지럽다...;
raw 문자열 사용하기
- row 문자열 : 원시 문자열
- \를 붙이지 않아도 특수 문자를 그대로 판단 가능
- 고로 \\숫자 처럼 \를 두번 붙이는 문자열은 \하나만 붙여서 사용가능
- 사용법 : r'\숫자 \g<이름> \g<숫자>'
re.sub('({\s*)"(\w+)":\s*"(\w+)"(\s*})', r'<\2>\3</\2>', '{ "name": "james" }')
'<name>james</name>'
그냥 일단 있다고 알아만 놓자..
'Python > 기초문법' 카테고리의 다른 글
(38)모듈과 패키지 만들기 (0) | 2022.05.07 |
---|---|
(37)모듈,패키지 사용하기 (0) | 2022.05.07 |
(35)데코레이터 사용하기 - 2 (0) | 2022.05.05 |
(34)데코레이터 사용하기 (0) | 2022.05.04 |
(33)코루틴(coroutine) 사용하기 (0) | 2022.05.03 |