정규표현식(regular expression)은 일정한 규칙(패턴)을 가진 문자열을 표현하는 방법이다. 복잡한 문자열 속에서 특정한 규칙으로 된 문자열을 검색한 뒤 추출하고 바꿀 때 사용하거나 문자열이 정해진 규칙에 맞는지 판단할 때 사용한다.
문자열에 특정 문자열이 포함되어 있는지 판단한다. 정규표현식은 re 모듈을 가져와서 사용하며 match 함수에 정규표현식 패턴과 판단할 문자열을 넣는다(re는 regular expression의 약자).
re.match('패턴', '문자열')
>>> import re
>>> re.match('Hello', 'Hello, world!') # 문자열이 있으므로 정규표현식 매치 객체가 반환됨
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
>>> re.match('Python', 'Hello, world!') # 문자열이 없으므로 아무것도 반환되지 않음
^문자열 : 문자열이 맨 앞에 오는지 판단한다.
문자열$ : 문자열이 맨 뒤에 오는지 판단한다(특정 문자열로 끝나는지).
이때는 match 대신 search 함수를 사용해야 한다.
match 함수 : 문자열 처음부터 매칭되는지 판단한다.
search 함수 : 문자열 일부분이 매칭되는지 판단다.
re.search('패턴', '문자열')
|는 특정 문자열에서 지정된 문자열(문자)이 하나라도 포함되는지 판단한다. 기본 개념은 OR 연산자와 같다.
문자열|문자열
문자열|문자열|문자열|문자열
>>> re.match('hello|world', 'hello') # hello 또는 world가 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='hello'>
문자열이 숫자로 되어있는지 판단하기 : 대괄호 안에 0-9로 숫자 범위를 표현하고 *는 문자(숫자)가 0개 이상 있는지, +는 1개 이상 있는지 판단한다.
[0-9]*
[0-9]+
? : 앞의 문자(범위)가 0개 또는 1개인지 판단한다.
. : .이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단한다.
문자?
[0-9]?
.
문자{개수}
(문자열){개수}
특정 범위의 문자(숫자)가 몇 개 있는지 판단할 때는 범위 [ ] 뒤에 {개수} 형식을 지정한다.
[0-9]{개수}
문자(숫자)의 개수 범위도 지정할 수 있다. 주로 휴대전화 번호 형식에 사용한다.
(문자){시작개수,끝개수}
(문자열){시작개수,끝개수}
[0-9]{시작개수,끝개수}
영문자 범위
a-z
A-Z
한글 범위
가-힣
단순히 숫자인지 문자인지 판단할 때
\d: [0-9]와 같음. 모든 숫자
\D: [^0-9]와 같음. 숫자를 제외한 모든 문자
\w: [a-zA-Z0-9_]와 같음. 영문 대소문자, 숫자, 밑줄 문자
\W: [^a-zA-Z0-9_]와 같음. 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자
[^범위]*
[^범위]+
'[^A-Z]+'는 대문자를 제외한 모든 문자(숫자)가 1개 이상 있는지 판단한다.
^[범위]*
^[범위]+
'^[A-Z]+'는 영문 대문자로 시작하는지 판단한다.
[범위]*$
[범위]+$
특수 문자 앞에 \를 붙인다. [ ] 안에서는 \를 붙이지 않아도 되지만 에러가 발생하는 경우에는 \를 붙인다.
>>> 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)'>
' ' 혹은 \s 또는 \S
\s: [ \t\n\r\f\v]와 같음. 공백(스페이스), \t(탭) \n(새 줄, 라인 피드), \r(캐리지 리턴), \f(폼피드), \v(수직 탭)을 포함
\S: [^ \t\n\r\f\v]와 같음. 공백을 제외하고 \t, \n, \r, \f, \v만 포함
정규표현식 그룹 : 해당 그룹과 일치하는 문자열을 얻어올 때 사용한다.
패턴 안에서 정규표현식을 ( )(괄호)로 묶으면 그룹이 된다.
(정규표현식) (정규표현식)
다음은 공백으로 구분된 숫자를 두 그룹으로 나누어서 찾은 뒤 각 그룹에 해당하는 문자열(숫자)을 가져온다.
매치객체.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'
groups 메서드는 각 그룹에 해당하는 문자열을 튜플로 반환한다.
매치객체.groups()
그룹 개수가 많아지면 숫자로 그룹을 구분하기가 힘들어져 그룹에 이름을 지는다. 그룹의 이름은 ( )(괄호) 안에 ?P<이름> 형식으로 지정한다.
(?P<이름>정규표현식)
매치객체.group('그룹이름')
>>> m = re.match('(?P<func>[a-zA-Z_][a-zA-Z0-9_]+)\((?P<arg>\w+)\)',
'print(1234)')
>>> 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']
sub 함수를 사용하며 패턴, 바꿀 문자열, 문자열, 바꿀 횟수를 넣어준다.
바꿀 횟수를 생략하면 찾은 문자열을 모두 바꾼다.
re.sub('패턴', '바꿀문자열', '문자열', 바꿀횟수)
>>> re.sub('apple|orange', 'fruit', 'apple box orange tree')
# apple 또는 orange를 fruit로 바꿈
'fruit box fruit tree'
>>> def multiple10(m): # 매개변수로 매치 객체를 받음
... n = int(m.group()) # 매칭된 문자열을 가져와서 정수로 변환
... return str(n * 10) # 숫자에 10을 곱한 뒤 문자열로 변환해서 반환
...
>>> 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('([a-z]+) ([0-9]+)', '\\2 \\1 \\2 \\1', 'hello 1234')
# 그룹 2, 1, 2, 1 순으로 바꿈
'1234 hello 1234 hello'
>>> re.sub('({\s*)"(?P<key>\w+)":\s*"(?P<value>\w+)"(\s*})', '<\\g<key>>\\g<value></\\g<key>>', '{ "name": "james" }')
'<name>james</name>'