-
일정한 규칙(패턴)을 가진 문자열을 표현하는 방법
문자열 판단하기
re 모듈을 가져와 사용(re는 regular expression의 약자)
>>> # re.match('패턴', '문자열') >>> >>> import re >>> >>> re.match('Hello', 'Hello, world') # 문자열이 있으므로 정규표현식 매치 객체 반환 <re.Match object; span=(0, 5), match='Hello'> >>> >>> re.match('python', 'Hello, world') # 문자열이 없으므로 아무것도 반환되지 않음 >>> >>> re.match('python', 'Hello, python') >>>
문자열이 맨 앞에 오는지 맨 뒤에 오는지 판단
^문자열: 문자열이 맨 앞에 오는지 판단 / 문자열$: 문자열이 맨 뒤에 오는지 판단(특정 문자열로 끝나는지)
이때는 match 대신 search 함수를 사용해야 함
match는 문자열 처음부터 매칭되는지 판단, search는 문자열 일부분이 매칭되는지 판단
>>> # re.search('패턴', '문자열') >>> >>> re.search('^Hello', 'Hello, world') # Hello로 시작하므로 패턴에 매칭됨 <re.Match object; span=(0, 5), match='Hello'> >>> >>> re.search('world$', 'Hello, world') # world로 끝나므로 패턴에 매칭됨 <re.Match object; span=(7, 12), match='world'> >>> >>> re.search('world$', 'Hello, world!') >>> >>> re.search('Python', 'Hello, Python') <re.Match object; span=(7, 13), match='Python'> >>> >>> re.search('world', 'Hello, world!') <re.Match object; span=(7, 12), match='world'> >>> >>> re.search('world', 'Hello world universe local') <re.Match object; span=(6, 11), match='world'>
지정된 문자열이 하나라도 포함되는지 판단
>>> # 문자열|문자열 / 문자열|문자열|문자열|문자열 >>> re.match('hello|world', 'hello') <re.Match object; span=(0, 5), match='hello'>
범위 판단하기
문자열이 숫자로 되어 있는지 판단, 숫자 범위는 0-9처럼 표현
>>> # [0-9]* : 문자(숫자)가 0개 이상 있는지 판단 >>> # [0-9]+ : 문자(숫자)가 1개 이상 있는지 판단 >>> >>> re.match('[0-9]*', '1234') # 1234는 0부터 9까지 숫자가 0개 이상 있으므로 패턴에 매칭됨 <re.Match object; span=(0, 4), match='1234'> >>> >>> re.match('[0-9]+', '1234') # 1234는 0부터 9까지 숫자가 1개 이상 있으므로 패턴에 매칭됨 <re.Match object; span=(0, 4), match='1234'> >>> >>> re.match('[0-9]+', 'abcd') # abcd는 0부터 9까지 숫자가 1개 이상 없으므로 패턴에 매칭 X >>> >>> re.match('[0-9]+', 'abcd1') # match는 맨 앞에서부터 찾기 때문에 패턴에 매칭 x >>> >>> re.match('[0-9]+', '1abcd') <re.Match object; span=(0, 1), match='1'>
*와 + 활용
>>> re.match('a*b', 'b') # b에는 a가 0개 이상 있으므로 패턴에 매칭 <re.Match object; span=(0, 1), match='b'> >>> >>> re.match('a+b', 'b') # b에는 a가 1개 이상 없으므로 패턴에 매칭 X >>> >>> re.match('a*b', 'aab') # aab에는 a가 0개 이상 있으므로 패턴에 매칭 <re.Match object; span=(0, 3), match='aab'> >>> >>> re.match('a+b', 'aab') # aab에는 a가 1개 이상 있으므로 패턴에 매칭 <re.Match object; span=(0, 3), match='aab'> >>> >>> re.match('a*b', 'baa') <re.Match object; span=(0, 1), match='b'> >>> >>> re.match('a+b', 'baa') >>> >>> re.match('a*b', 'ab') <re.Match object; span=(0, 2), match='ab'> >>> >>> re.match('a*b', 'ba') <re.Match object; span=(0, 1), match='b'> >>> >>> re.match('a+b', 'ab') <re.Match object; span=(0, 2), match='ab'> >>> >>> re.match('a+b', 'ba') >>> >>> re.match('ba+', 'ba') <re.Match object; span=(0, 2), match='ba'> >>>
a*b, a+b에서 b는 무조건 있어야 하는 문자
> a*는 a가 0개 이상 있어야 하므로 b 매칭, a+는 a가 1개 이상 있어야 하므로 b는 매칭 X
문자가 한 개만 있는지 판단
>>> # 문자?, [0-9]? : ? 앞의 문자(범위)가 0개 또는 1개인지 판단 >>> # . : .이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단 >>> >>> re.match('abc?d', 'abd') # abd에서 c 위치에 c가 0개 있으므로 패턴에 매칭 <re.Match object; span=(0, 3), match='abd'> >>> >>> re.match('ab[0-9]?c', 'ab3c') # [0-9] 위치에 숫자가 1개 있으므로 패턴에 매칭 <re.Match object; span=(0, 4), match='ab3c'> >>> >>> re.match('ab.d', 'abxd') # .이 있는 위치에 문자가 1개 있으므로 패턴에 매칭 <re.Match object; span=(0, 4), match='abxd'> >>> >>> re.match('ab.d', 'abx1d') >>> >>> re.match('ab..d', 'abx1d') <re.Match object; span=(0, 5), match='abx1d'>
문자 개수 판단
>>> # 문자{개수} / (문자열){개수} >>> >>> re.match('h{3}', 'hhhello') <re.Match object; span=(0, 3), match='hhh'> >>> >>> re.match('(hello){3}', 'hellohellohelloworld') <re.Match object; span=(0, 15), match='hellohellohello'> >>> >>> re.match('h{3}', 'hhhhello') <re.Match object; span=(0, 3), match='hhh'> >>> >>> re.match('h{3}', 'hhello') >>> >>> re.match('(hello){3}', 'hello hello hello world') >>>
특정 범위의 문자(숫자)가 몇 개 있는지 판단 가능
>>> # [0-9]{개수} >>> >>> # 문자(숫자)의 개수 범위도 지정 가능 {시작개수,끝개수} 형식 >>> # (문자){시작개수,끝개수} / (문자열){시작개수,끝개수} / [0-9]{시작개수,끝개수} >>> >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-1000') # 숫자 3개-4개-4개 <re.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개-3개 >>> >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010.1000.1000') >>> >>> re.match('[0-9]{3}.[0-9]{4}.[0-9]{4}', '010.1000.1000') <re.Match object; span=(0, 13), match='010.1000.1000'> >>> >>> re.match('[0-9]{3}.[0-9]{4}.[0-9]{4}', '010--1000--1000') >>> >>> re.match('[0-9]{3}..[0-9]{4}..[0-9]{4}', '010--1000--1000') <re.Match object; span=(0, 15), match='010--1000--1000'> >>> >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-100-1000') <re.Match object; span=(0, 11), match='02-100-1000'> >>> >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-1000-1000') <re.Match object; span=(0, 12), match='02-1000-1000'> >>> >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-10-1000') >>>
숫자와 영문 문자를 조합해 판단
>>> # a-z / A-Z >>> >>> re.match('[a-zA-Z0-9]+', 'Hello1234') # a부터 z, A부터 Z, 0부터 9까지 1개 이상 <re.Match object; span=(0, 9), match='Hello1234'> >>> >>> re.match('[A-Z0-9]+', 'hello') # A부터 Z, 0부터 9까지 1개 이상 / 대문자, 숫자는 없고 소문자만 있어 매칭 x >>> >>> # 가-힣 >>> >>> re.match('[가-힣]+', '홍길동') # 가부터 힣까지 1개 이상 <re.Match object; span=(0, 3), match='홍길동'> >>> >>> re.match('[가-힣]+', '아헿헷') # 가부터 힣까지 1개 이상 <re.Match object; span=(0, 3), match='아헿헷'>
특정 문자 범위에 포함되지 않는지 판단
>>> # 특정 문자 범위에 포함되지 않는지 판단(제외) >>> # [^범위]* / [^범위]+ >>> >>> re.match('[^A-Z]+', 'Hello') # 대문자를 제외 >>> >>> re.match('[^A-Z]+', 'hello') # 대문자를 제외, 대문자가 없으므로 패턴에 매칭 <re.Match object; span=(0, 5), match='hello'>
범위 제외할 때는 '[^A-z]+'와 같이 [] 안에 넣어 주고, 특정 문자 범위로 시작할 때는 '^[A-Z]+'와 같이 [] 앞에 붙여 줌
>>> # 특정 문자 범위로 시작 >>> # ^[범위]* / ^[범위]+ >>> re.search('^[A-Z]+', 'Hello') # 대문자로 시작하므로 패턴에 매칭 <re.Match object; span=(0, 1), match='H'> >>> >>> # 특정 문자 범위로 끝나는지 확인 >>> # [범위]*$ / [범위]+$ >>> >>> re.search('[0-9]+$', 'Hello1234') # 숫자로 끝나므로 패턴에 매칭 <re.Match object; span=(5, 9), match='1234'>
특수 문자 판단
>>> # \특수문자 >>> # [] 안에서는 \를 붙이지 않아도 되지만 에러가 발생하는 경우 \를 붙임 >>> >>> re.search('\*+', '1 ** 2') # *가 들어 있는지 판단 <re.Match object; span=(2, 4), match='**'> >>> >>> re.match('[$()a-zA-Z0-9]+', '$(document0') # $, (, )와 문자, 숫자가 들어 있는지 판단 <re.Match object; span=(0, 11), match='$(document0'> >>>
범위를 지정하면서 a-zA-Z0-9처럼 대소문자와 숫자를 모두 나열했는데 이런 방식으로 범위를 정하면 정규표현식이 길고 복잡해짐
숫자인지 문자인지 판단할 때는 \d, \D, \w, \W를 사용
\d: [0-9]와 같음, 모든 숫자
\D: [^0-9]와 같음
\w: [a-zA-Z0-9_]와 같음, 영문 대소문자, 숫자, 밑줄 문자
\W: [^a-zA-Z0-9_]와 같음, 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자
>>> re.match('\d+', '1234') # 모든 숫자이므로 패턴에 매칭 <re.Match object; span=(0, 4), match='1234'> >>> >>> re.match('\D+', 'hello') # 숫자를 제외한 모든 문자이므로 패턴에 매칭 <re.Match object; span=(0, 5), match='hello'> >>> >>> re.match('\w+', 'Hello_1234') # 영문 대소문자, 숫자, 밑줄 문자이므로 패턴에 매칭 <re.Match object; span=(0, 10), match='Hello_1234'> >>> >>> re.match('\W+', '(:)') # 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자이므로 패턴에 매칭 <re.Match object; span=(0, 3), match='(:)'>
공백 처리
공백은 ' '처럼 공백 문자를 넣어도 되고, \s 또는 \S로 표현할 수 있음
\s: [\t\n\r\f\v]와 같음. 공백(스페이스), \t(탭), \n(새 줄, 라인 피드), \r(캐리지 리턴), \f(폼피드), \v(수직 탭)을 포함
\S: [^ \t\n\r\f\v]와 같음. 공백을 제외하고 \t, \n, \r, \f, \v만 포함
캐리지 리턴: 인쇄 위치 또는 커서 표시 위치를 같은 줄(행) 맨 앞의 위치로 복귀
라인 피드: 커서의 위치를 아랫줄로 이동
폼 피드: 프린터에 보내면 연속용지가 담 페이지의 시작 부분으로 넘겨짐
>>> re.match('[a-zA-Z0-9 ]+', 'Hello 1234') # ' '로 공백 표현 <re.Match object; span=(0, 10), match='Hello 1234'> >>> >>> re.match('[a-zA-Z0-9\s]+', 'Hello 1234') # \s로 공백 표현 <re.Match object; span=(0, 10), match='Hello 1234'>
같은 정규표현식 패턴을 자주 사용할 때
매번 match나 search 함수에 정규표현식 패턴 지정하는 것은 비효율적
같은 패턴 자주 사용할 때는 compile 함수를 사용해 정규표현식 패턴을 객체로 만든 뒤 match 또는 search 메서드 호출
>>> # 객체 = re.compile('패턴') >>> # 객체.match('문자열') >>> # 객체.search('문자열') >>> >>> p = re.compile('[0-9]+') # 정규표현식 패턴을 객체로 만듦 >>> p.match('1234') # 정규표현식 패턴 객체에서 match 메서드 사용 <re.Match object; span=(0, 4), match='1234'> >>> p.search('hello') # 정규표현식 패턴 객체에서 search 메서드 사용 >>>
그룹 사용
>>> # 패턴 안에서 정규표현식을 ()(괄호)로 묶으면 그룹이 됨 >>> # (정규표현식) (정규표현식) >>> # 공백으로 구분된 숫자를 두 그룹으로 나누어 찾은 뒤 각 그룹에 해당하는 문자열(숫자) 가져옴 >>> >>> 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'
매치 객체의 group 메서드에 숫자를 지정하면 해당 그룹에 매칭된 문자열을 반환
숫자를 지정하지 않거나 0을 지정하면 매칭된 문자열을 한꺼번에 반환
groups 메서드는 각 그룹에 해당하는 문자열을 튜플로 반환
>>> # 매치객체.groups() >>> >>> m.groups() ('10', '295')
패턴에 매칭되는 모든 문자열 가져오기
그룹 지정 없이 패턴에 매칭되는 모든 문자열 가져오기
> findall 함수를 사용해 매칭된 문자열을 리스트로 반환
>>> # re.findall('패턴', '문자열') >>> >>> re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8') ['1', '2', '4', '7', '8'] >>> >>> re.findall('[a-z]+', '1 2 Fizz 4 Buzz Fizz 7 8') ['izz', 'uzz', 'izz'] >>> >>> re.findall('[A-Z]+', '1 2 Fizz 4 Buzz Fizz 7 8') ['F', 'B', 'F'] >>> >>> re.findall('[a-zA-Z]+', '1 2 Fizz 4 Buzz Fizz 7 8') ['Fizz', 'Buzz', 'Fizz'] >>> >>> re.findall('[a-zA-Z0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8') ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8'] >>>
문자열 바꾸기
정규표현식으로 특정 문자열 찾은 뒤 다른 문자열로 바꾸는 방법, sub 함수 사용
>>> # re.sub('패턴', '바꿀문자열', '문자열', 바꿀횟수) >>> # 바꿀 횟수를 넣으면 지정된 횟수만큼 바꾸고, 생략할 경우 찾은 문자열 모두 바꿈 >>> >>> re.sub('apple|orange', 'fruit', 'apple box orange tree') # apple 또는 orange를 fruit로 바꿈 '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.subb('패턴', 교체함수, '문자열', 바꿀횟수) >>> >>> 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'
'Python' 카테고리의 다른 글
모듈과 패키지 만들기 (0) 2023.10.30 모듈과 패키지 사용 (0) 2023.10.30 데코레이터(장식자) (0) 2023.10.25 코루틴 (0) 2023.10.24 제너레이터(발생자) (0) 2023.10.24