43.2 범위 판단하기
이번에는 문자열이 숫자로 되어있는지 판단해보겠습니다. 다음과 같이 [ ](대괄호) 안에 숫자 범위를 넣으며 * 또는 +를 붙입니다. 숫자 범위는 0-9처럼 표현하며 *는 문자(숫자)가 0개 이상 있는지, +는 1개 이상 있는지 판단합니다.
- [0-9]*
- [0-9]+
>>> 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개 이상 없으므로 패턴에 매칭되지 않음
그럼 *와 +는 어디에 활용할까요? 다음과 같이 a*b와 a+b를 확인해보면 쉽게 알 수 있습니다.
>>> 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'>
a*b, a+b에서 b는 무조건 있어야 하는 문자고, a*는 a가 0개 이상 있어야 하므로 b는 매칭이 됩니다. 하지만 a+는 a가 1개 이상 있어야 하므로 b는 매칭되지 않습니다. 그리고 'ab', 'aab', 'aaab'처럼 a가 0개 이상 또는 1개 이상 있을 때는 a*b와 a+b를 모두 만족합니다.
43.2.1 문자가 한 개만 있는지 판단하기
문자가 여러 개 있는지 판단할 때는 *과 +를 사용했지만, 문자가 한 개만 있는지 판단할 때는 어떻게 해야 할까요? 이때는 ?와 .을 사용합니다. ?는 ? 앞의 문자(범위)가 0개 또는 1개인지 판단하고, .은 .이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단합니다.
- 문자?
- [0-9]?
- .
>>> 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'>
43.2.2 문자 개수 판단하기
그럼 문자(숫자)가 정확히 몇 개 있는지 판단하고 싶을 수도 있겠죠? 이때는 문자 뒤에 {개수} 형식을 지정합니다. 문자열의 경우에는 문자열을 괄호로 묶고 뒤에 {개수} 형식을 지정합니다.
- 문자{개수}
- (문자열){개수}
h{3}은 h가 3개 있는지 판단하고, (hello){3}은 hello가 3개 있는지 판단합니다.
>>> 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') # 숫자 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]{시작개수,끝개수}
다음은 일반전화의 번호 형식에 맞는지 판단합니다.
>>> 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개 패턴에 매칭되지 않음
43.2.3 숫자와 영문 문자를 조합해서 판단하기
지금까지 숫자 범위만 판단해보았으니 이제 숫자와 영문 문자를 조합해서 판단해보겠습니다. 영문 문자 범위는 a-z, A-Z와 같이 표현합니다.
- a-z
- A-Z
>>> 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-zA-Z0-9 또는 A-Z0-9와 같이 붙여 쓰면 됩니다.
그럼 한글은 어떻게 사용할까요? 영문 문자와 방법이 같습니다. 가-힣처럼 나올 수 있는 한글 조합을 정해주면 됩니다.
- 가-힣
>>> re.match('[가-힣]+', '홍길동') # 가부터 힣까지 1개 이상 있으므로 패턴에 매칭됨 <_sre.SRE_Match object; span=(0, 3), match='홍길동'>
43.2.4 특정 문자 범위에 포함되지 않는지 판단하기
지금까지 정규표현식으로 특정 문자 범위에 포함되는지 살펴보았습니다. 그럼 특정 문자 범위에 포함되지 않는지 판단하려면 어떻게 해야 할까요? 다음과 같이 문자(숫자) 범위 앞에 ^를 붙이면 해당 범위를 제외합니다.
- [^범위]*
- [^범위]+
즉, '[^A-Z]+'는 대문자를 제외한 모든 문자(숫자)가 1개 이상 있는지 판단합니다.
>>> re.match('[^A-Z]+', 'Hello') # 대문자를 제외. 대문자가 있으므로 패턴에 매칭되지 않음 >>> re.match('[^A-Z]+', 'hello') # 대문자를 제외. 대문자가 없으므로 패턴에 매칭됨 <_sre.SRE_Match object; span=(0, 5), match='hello'>
앞에서 특정 문자열로 시작하는지 판단할 때도 ^를 사용했었는데 문법이 비슷해서 이 부분은 헷갈리기 쉽습니다. 범위를 제외할 때는 '[^A-Z]+'와 같이 [ ] 안에 넣어주고, 특정 문자 범위로 시작할 때는 '^[A-Z]+'와 같이 [ ] 앞에 붙여줍니다. 그래서 다음과 같이 '^[A-Z]+'는 영문 대문자로 시작하는지 판단합니다.
- ^[범위]*
- ^[범위]+
>>> re.search('^[A-Z]+', 'Hello') # 대문자로 시작하므로 패턴에 매칭됨 <_sre.SRE_Match object; span=(0, 1), match='H'>
물론 특정 문자(숫자) 범위로 끝나는지 확인할 때는 정규표현식 뒤에 $를 붙이면 됩니다.
- [범위]*$
- [범위]+$
>>> re.search('[0-9]+$', 'Hello1234') # 숫자로 끝나므로 패턴에 매칭됨 <_sre.SRE_Match object; span=(5, 9), match='1234'>
43.2.5 특수 문자 판단하기
문자열을 판단할 때 'Hello1234'처럼 평범한 문자열만 판단했습니다. 그런데 정규표현식에 사용하는 특수 문자 *, +, ?, ., ^, $, (, ) [, ], - 등을 판단하려면 어떻게 해야 할까요? 특수 문자를 판단할 때는 특수 문자 앞에 \를 붙이면 됩니다. 단, [ ] 안에서는 \를 붙이지 않아도 되지만 에러가 발생하는 경우에는 \를 붙입니다.
- \특수문자
>>> 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)'>
지금까지 범위를 지정하면서 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') # 모든 숫자이므로 패턴에 매칭됨 <_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='(:)'>
43.2.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만 포함
>>> 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'>
매번 match나 search 함수에 정규표현식 패턴을 지정하는 방법은 비효율적입니다. 같은 패턴을 자주 사용할 때는 compile 함수를 사용하여 정규표현식 패턴을 객체로 만든 뒤 match 또는 search 메서드를 호출하면 됩니다.
객체 = re.compile('패턴')
객체.match('문자열')
객체.search('문자열')
>>> p = re.compile('[0-9]+') # 정규표현식 패턴을 객체로 만듦 >>> p.match('1234') # 정규표현식 패턴 객체에서 match 메서드 사용 <_sre.SRE_Match object; span=(0, 4), match='1234'> >>> p.search('hello') # 정규표현식 패턴 객체에서 search 메서드 사용