-
[코딩도장] day30. 파이썬 정규표현식 사용하기 - re, *, +, matchIT/파이썬 2020. 9. 29. 07:36
■ 들어가기
- 파이썬 코딩 도장 (남재윤/길벗). 을 공부하며 정리하는 블로그
▶ Unit43. 정규표현식 사용하기
- 43.1 문자열 판단하기
- 43.2 범위 판단하기
- 43.3 그룹 사용하기
- 43.4 문자열 바꾸기
0. 들어가기
- 정규표현식(regular expression) : 일정한 규칙(패턴)을 가진 문자열을 표현하는 방법
- 복잡한 문자열 속에서 특정한 규칙으로 된 문자열을 검색한 뒤 추출하거나 바꿀 때 사용
- 또는 문자열이 정해진 규칙에 맞는지 판단하는 용도로 사용
>> 43.1 문자열 판단하기 <<
- 정규표현식은 re 모듈을 가져와서 사용하며
- match 함수에 정규표현식 패턴과 판단할 문자열을 넣음 (re : regular expression의 약자)
- 사용법: re.match('패턴', '문자열')
>>> import re
>>> re.match('Hello', 'Hello world!') # Hello world! 문자열에 Hello가 있는지 판단 (있으면 정규표현식 객체가 반환)
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
>>> re.match('Python', 'Hello world!') # Hello world! 문자열에 Python이 있는지 판단 (없으면 아무것도 반환하지 않음)
>>>- 문자열이 있으면 매치(SRE_Match) 객체가 반환, 없으면 아무것도 반환되지 않음
- 'Hello world!'.find('Hello') 처럼 문자열 메서드로도 사용 가능
1. 문자열이 맨 앞에 오는지 맨 뒤에 오는지 판단하기
- 문자열 앞에 ^를 붙이면 문자열이 맨 앞에 오는지 판단하고
- 문자열 뒤에 $를 붙이면 문자열이 맨 뒤에 오는지 판단(=> 특정 문자열로 끝나는지)
- 사용법
^문자열
문자열$
- 위의 패턴 이용 시 search 함수 사용해야 함
- match 함수는 문자열 처음부터 매칭되는지 판단, search 함수는 문자열 일부분이 매칭되는지 판단
- 사용법 : re.search('패턴', '문자열')
>>> import re
>>> re.search('^Hello', 'Hello, world!') # Hello로 시작하므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
>>> re.search('world!$', 'Hello, world!') # world!로 끝나므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(7, 13), match='world!'>2. 지정된 문자열이 하나라도 포함되는지 판단하기
- | 는 특정 문자열에서 지정된 문자열(문자)이 하나라도 포함되는지 판단 (OR 연산자와 동일)
- 사용법
문자열|문자열
문자열|문자열|문자열|문자열
>>> import re
>>> re.match('hello|world', 'hello') # hello 또는 world가 있으므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='hello'>>> 43.2 범위 판단하기 <<
- 문자열이 숫자로 되어있는지 판단하기 위해서는
- [](대괄호) 안에 숫자 범위를 넣고, * 또는 + 를 붙힘
- 숫자 범위는 0-9 와 같이 표현하고 *는 문자(숫자)가 0개 이상 있는지, +는 1개 이상 있는지 판단
- 사용법
[0-9]*
[0-9]+
>>> import re
>>> 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') # abcd는 0부터 9까지 숫자가 1개 이상 없으므로 패턴에 매칭되지 않음
>>>- * 와 + 활용 예제
>>> import re
>>> re.match('a*b', 'b') # b에는 a가 0개 이상 있으므로 패턴에 매칭
<_sre.SRE_Match object; span=(0, 1), match='b'>
>>> re.match('a+b', 'b') # b에는 a가 1개 이상 없으므로 패턴에 매칭되지 않음
>>> 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'>
>>>1. 문자가 한 개만 있는지 판단하기
- 문자가 여러 개 있는지 판단할 때는 * 와 + 를 사용했음
- 문자가 한 개만 있는지 판단할 때는 ? 와 . 를 사용함
- ? 는 ? 앞의 문자(범위)가 0개 또는 1개인지 판단
- .은 . 이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단
- 사용법
문자?
[0-9]?
.
>>> import re
>>> 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'>2. 문자 개수 판단하기
- 문자(숫자)가 정확히 몇 개 있는지 판단하고 싶은 경우, 문자 뒤에 {개수} 형식으로 지정
- 문자열의 경우 문자열을 괄호로 묶고 뒤에 {개수} 형식을 지정
- 사용법
문자{개수}
(문자열){개수}
>>> import re
>>> re.match('h{3}', 'hhhello') # h가 3개 있는지 판단하며, 일치하는 경우 결과 반환
<_sre.SRE_Match object; span=(0, 3), match='hhh'>
>>> re.match('h{3}', 'hello') # 불일치하는 경우 반환값 없음
>>>
>>> re.match('(hello){3}', 'hellohellohelloworld') # hello가 3개 있는지 판단
<_sre.SRE_Match object; span=(0, 15), match='hellohellohello'>- 특정 범위의 문자(숫자)가 몇 개 있는지 판단 가능하며 범위 [] 뒤에 {개수} 형식을 지정
- 사용법 : [0-9]{개수}
>>> import re
>>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-1000') # 숫자패턴 3개-4개-4개 형식 판단
<_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') # 숫자패턴 3개-4개-4개 형식 판단, 불일치
>>>- 문자(숫자)의 개수 범위 지정 가능하며, {시작개수,끝개수} 형식을 지정하여 판단
- 사용법
(문자){시작개수,끝개수}
(문자열){시작개수,끝개수}
[0-9]{시작개수,끝개수}
>>> import re
>>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-100-1000') # 숫자 2~3개-3~4개-4개 형식 판단
<_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') # 숫자 2~3개-3~4개-4개 형식 판단, 불일치
>>>3. 숫자와 영문 문자를 조합해서 판단하기
- 영문 문자 범위 표기 : a-z, A-Z
>>> import re
>>> re.match('[a-zA-Z0-9]+', 'Hello1234') # a~z, A~Z, 0~9까지 1개 이상있는지 판단
<_sre.SRE_Match object; span=(0, 9), match='Hello1234'>
>>> re.match('[A-Z0-9]+', 'hello') # A~Z, 0~9까지 1개 이상 있는지 판단, 불일치
>>>- 한글 문자 범위 표기 : 가-힣
>>> import re
>>> re.match('[가-힣]+', '홍길동') # 가~힣까지 1개 이상 있는지 판단, 매칭
<_sre.SRE_Match object; span=(0, 3), match='홍길동'>4. 특정 문자 범위에 포함되지 않는지 판단
- 특정 문자(숫자) 범위 포함 제외는 범위 앞에 ^ 를 붙히면 됨
- 사용법
[^범위]*
[^범위]+
- 예제 : [^A-Z]+ /* 대문자를 제외한 모든 문자(숫자)가 1개 이상 있는지 판단 => 즉, 대문자가 아닌 문자(숫자)로 이루어졌는지 확인 */
>>> import re
>>> re.match('[^A-Z]+', 'Hello') # 대문자를 제외한 문자로 구성되었는지 판단. 아니므로 반환값 없음
>>> re.match('[^A-Z]+', 'hello') # 대문자가 아닌 소문자로 구성되었기 때문에 패턴에 매칭
<_sre.SRE_Match object; span=(0, 5), match='hello'>[잠깐!] ^ 의 사용 범위 - 시작 vs 제외 ??
- 특정 문자열로 시작하는지 판단 : [](대괄호) 앞에 붙혀줌
- 특정 문자(숫자) 범위 제외할 때 : [](대괄호) 안에 넣어줌
>>> import re
>>> re.match('^[A-Z]+', 'Hello') # 대문자로 시작하는지 판단. 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 1), match='H'>
>>> re.match('[^A-Z]+', 'Hello') # 대문자를 제외한 문자가 포함되었는지 판단. 패턴에 매칭되지 않음
>>>- 특정 문자(숫자) 범위로 끝나는지 확인 : 정규표현식 뒤에 $를 붙힘
- 단, match 함수가 아닌 search 함수를 사용해야 함
- match 함수는 문자열 처음부터 매칭되는지 판단
- search 함수는 문자열 일부분이 매칭되는지 판단 (문자열 맨 앞, 맨 뒤 판단 시에 사용!)
[범위]*$
[범위]+$
>>> import re
>>> re.search('[0-9]+$', 'Hello1234')
<_sre.SRE_Match object; span=(5, 9), match='1234'>5. 특수 문자 판단하기
- 특수 문자 판단 시, 특수 문자 앞에 \ 를 붙힘
- 사용법 : \특수문자
>>> import re
>>> 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)'>
>>> re.match('[\$()a-zA-Z0-9]+', '$(document)') # [] 내에서는 \ 제외 가능하며, \를 사용해도 동일한 결과
<_sre.SRE_Match object; span=(0, 11), match='$(document)'>- a-zA-Z0-9 : 영문 대소문자와 숫자.를 의미하며 아래와 같이 축약하여 표기 가능
\d: [0-9]와 동일. 모든 숫자
\D: [^0-9]와 동일. 숫자를 제외한 모든 문자
\w: [a-zA-Z0-9_]와 동일. 영문 대소문자, 숫자, 밑줄 문자
\W: [^a-zA-Z0-9_]와 동일. 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자
>>> import re
>>> re.match('\d+', '1234') # 모든 숫자인지 판단. 매칭
<_sre.SRE_Match object; span=(0, 4), match='1234'>
>>> re.match('\D+', 'Hello') # 숫자를 제외한 모든 문자인지 판단. 매칭
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
>>> re.match('\w+', 'Hello_1234') # 영문 대소문자, 숫자, 밑줄 문자인지 판단. 매칭
<_sre.SRE_Match object; span=(0, 10), match='Hello_1234'>
>>> re.match('\W+', '(:)') # 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자인지 판단
<_sre.SRE_Match object; span=(0, 3), match='(:)'>6. 공백 처리하기
- 표현법 : ' ', \s, \S
\s : [ \t\n\r\f\v]와 동일. 공백(스페이스), \t(탭), \n(새 줄), \r(캐리지 리턴), \f(폼피드), \v(수직 탭) 포함
\S : [^ \t\n\r\f\v]와 동일. 공백 제외하고 \t, \n, \r, \f, \v 만 포함
>>> import re
>>> 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'>[참고] 동일 정규표현식 자주 사용할 때!
- compile 함수를 사용하여 정규표현식 패턴을 객체로 만든 뒤, match 또는 search 함수를 호출!
- 사용법
객체 = re.compile('패턴')
객체.match('문자열')
객체.search('문자열')
>>> import re
>>> p = re.compile('[0-9]+') # 정규표현식 패턴을 객체로 만듦
>>> p.match('1234') # 정규표현식 패턴 객체에서 match 함수 호출. 패턴 매칭
<_sre.SRE_Match object; span=(0, 4), match='1234'>
>>> p.search('hello') # 정규표현식 패턴 객체에서 search 함수 호출. 패턴 미매칭
>>>>> 43.3 그룹 사용하기 <<
- 정규표현식을 개별이 아닌 그룹으로 묶는 방법
- 패턴 안에서 정규표현식을 ()(괄호)로 묶으면 그룹이 됨
- 사용법 : (정규표현식) (정규표현식)
>>> import re
>>> m = re.match('([0-9]+) ([0-9]+)', '10 295')
>>> m.group(1)
'10'
>>> m.group(2)
'295'
>>> m.group()
'10 295'
>>> m.group(0)
'10 295'- group 함수에 숫자를 지정하면 해당 그룹에 매칭된 문자열을 반환
- 숫자를 지정하지 않거나 0을 지정하면 매칭된 문자열을 한꺼번에 반환
매치객체.group(숫자)
- 앞서 사용한 compile 함수를 활용하여 객체에 담아 사용 가능
>>> p = re.compile('([0-9]+) ([0-9]+)')
>>> p.match('10 295')
<_sre.SRE_Match object; span=(0, 6), match='10 295'>
>>> q = p.match('10 295')
>>> q.group(1)
'10'
>>> q.group(2)
'295'
>>> q.group()
'10 295'
>>> q.group(0)
'10 295'- groups 함수는 각 그룹에 해당하는 문자열을 튜플로 반환
매치객체.groups()
>>> m.groups() # 각 그룹에 해당하는 문자열을 튜플 형태로 반환
('10', '295')- 그룹 개수가 많아지면 숫자로 구분하기 어렵기 때문에 그룹에 이름을 지정하여 사용
(?P<이름>정규표현식)
>>> import re
>>> 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'
>>> m.group()
'print(1234)'매치객체.group('그룹이름')
- 각 그룹에 설정한 이름을 group 함수에 이름을 지정하여 호출
- 함수 이름은 영문자로 시작하기 때문에 [a-zA-Z_] 로 표현, 이후 영문자, 숫자 포함된 문자 판단
- 괄호는 \(, \) 로 판단
1. 패턴에 매칭되는 모든 문자열 가져오기
- 그룹 지정 없이 패턴에 매칭되는 모든 문자열 가져올 때는 findall 함수를 사용하여 매칭된 문자열을 리스트로 반환
- 사용법 : re.findall('패턴', '문자열')
>>> import re
>>> re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8') # 문자열에서 숫자만 가져와서 리스트로 반환
['1', '2', '4', '7', '8'][참고] *, +, 그룹 활용하기
- 보통 정규표현식에서 +와 *을 조합하여 사용할 때는 그룹으로 묶어서 사용
- 예를 들어 (.[a-z]+)* 는 점(.)과 영문 소문자가 1개 이상 있는지 판단하고, 이 묶음이 0개 이상인지 판단
- 규칙은 반드시 지켜야 하지만 있어도 되고 없어도 되는 상황에서 사용
>>> import re
>>> re.match('[a-z]+(.[a-z]+)*$', 'hello.world') # 영문 소문자 1개 이상, 점과 영문 소문자1개 이상으로 끝나는지 판단
<_sre.SRE_Match object; span=(0, 11), match='hello.world'> # .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'>>> 43.4 문자열 바꾸기 <<
- 정규표현식으로 특정 문자열을 찾은 뒤, 다른 문자열로 바꾸는 방법
- 문자열 변경 시 sub 함수를 사용하며 패턴, 바꿀 문자열, 문자열, 바꿀 횟수를 넣어줌
- 바꿀 횟수 지정 시 지정된 횟수만큼 변경, 바꿀 횟수 생략 시 찾은 문자열 모두 변경
- 사용법
re.sub('패턴', '바꿀문자열', '문자열', 바꿀횟수)
>>> import re
>>> re.sub('apple|orange', 'fruit', 'apple box orange tree') # apple 또는 orange를 fruit로 바꿈
'fruit box fruit tree'
>>> re.sub('apple|orange', 'fruit', 'apple box orange tree', 1) # apple 또는 orange를 fruit으로 1회만 바꿈
'fruit box orange tree'
>>> re.sub('apple|orange', 'fruit', 'apple box orange tree', 2) # apple 또는 orange를 fruit으로 2회만 바꿈
'fruit box fruit tree'
>>> re.sub('[0-9]+', 'n', '1 2 Fizz 4 Buzz Fizz 7 8') # 숫자를 찾아 n 으로 변경
'n n Fizz n Buzz Fizz n n'- sub 함수는 바꿀 문자열 대신에 교체 함수 지정 가능
- 교체 함수는 매개변수로 매치 객체를 받으며, 결과를 문자열로 반환
교체함수(매치객체)
re.sub('패턴', 교체함수, '문자열', 바꿀횟수)
>>> import re
>>> 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'- 위의 sub 함수 내용을 람다 표현식으로 사용 가능
>>> 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'1. 찾은 문자열을 결과에 다시 사용하기
- 먼저 정규표현식을 그룹으로 묶음
- 바꿀 문자열에서 \\숫자 형식으로 매칭된 문자열을 가져와서 사용 가능
>>> import re
>>> re.sub('([a-z]+) ([0-9]+)', '\\2 \\1 \\2 \\1', 'hello 1234') # hello는 그룹1, 1234는 그룹2로 찾은 뒤 2, 1, 2, 1 순으로 변경
'1234 hello 1234 hello'- 특수기호 포함된 문자열 변경
>>> import re
>>> re.sub('({\s*)"(\w+)":\s*"(\w+)"(\s*})', '<\\2>\\3</\\2>', '{ "name": "james" }')
'<name>james</name>'- ({\s*) 는 { 와 공백(\s)을 찾음 => '{ '
- (\s*}) 는 공백(\s)과 } 을 찾음 => ' }'
- "(\w+)":\s*"(\w+)" 는 : 을 기준으로 양 옆의 name과 james를 찾음
- 바꿀 문자열은 그룹 2, 그룹3, 그룹2 의 순으로 설정
'IT > 파이썬' 카테고리의 다른 글
[코딩도장] day33. 파이썬: 모듈과 패키지 만들기 (0) 2020.10.09 [코딩도장] day32. 파이썬 모듈과 패키지 사용하기 (0) 2020.10.06 [코딩도장] day29. 파이썬 데코레이터(2/2) (0) 2020.09.26 [코딩도장] day28. 파이썬 데코레이터(1/2) - @데코레이터, def데코레이터 (0) 2020.09.22 [코딩도장] day27. 파이썬 코루틴 - next, send, yield (0) 2020.09.21