2.23. 내용정리: 26일차
흔한 찐따 edited this page May 23, 2022
·
2 revisions
- 정규 표현식은 특정한 규칙을 가진 문자열의 패턴을 표현하는 데 사용하는 표현식(Expression)으로 텍스트에서 특정 문자열을 검색하거나 치환할 때 흔히 사용된다.
- 예를 들어, 웹 페이지에서 전화번호나 이메일 주소를 발췌한다거나 로그 파일에서 특정 에러 메시지가 들어간 라인들을 찾을 때 정규 표현식을 사용하면 쉽게 구현할 수 있다.
- 정규 표현식은 간단히 정규식 혹은 Regex 등으로 불리우곤 한다.
- 정규식에서 가장 단순한 것은 특정 문자열을 직접 리터럴로 사용하여 해당 문자열을 검색하는 것이다.
- 예를 들어, 로그 파일에
"에러 1033"
이라는 문자열을 검색하여 이 문자열이 있으면 이를 출력하고 없으면None
을 리턴하는 경우이다. - 이러한 간단한 검색을 파이썬에서 실행하는 방법은 아래와 같다.
- 먼저 파이썬에서 정규표현식을 사용하기 위해서는 Regex를 위한 모듈인
re
모듈을 사용한다. -
re
모듈의compile
함수는 정규식 패턴을 입력으로 받아들여 정규식 객체를 리턴한다. - 즉
re.compile(검색할문자열)
와 같이 함수를 호출하면 정규식 객체 (re.RegexObject
클래스 객체)를 리턴하게 된다. -
re.RegexObject
클래스는 여러 메서드들을 가지고 있는데, 이 중 여기서는 특정 문자열을 검색하여 처음 맞는 문자열을 리턴하는search()
메서드를 사용해 본다. - 이
search()
는 처음 매칭되는 문자열만 리턴하는데, 매칭되는 모든 경우를 리턴하려면findall()
을 사용한다. -
search()
는 검색 대상이 있으면 결과를 갖는MatchObject
객체를 리턴하고, 맞는 문자열이 없으면None
을 리턴한다. -
MatchObject
객체로부터 실제 결과 문자열을 얻기 위해서는group()
메서드를 사용한다. - 자세한 내용은 파이썬 공식 문서의 정규식 HOWTO 문서를 참고하면 된다.
- 또한,
re
모듈에 대한 사용법과 상세한 설명은 re — 정규식 연산 문서를 참고하면 된다.
import re
text = "에러 1122 : 레퍼런스 오류\n 에러 1033: 아규먼트 오류"
regex = re.compile("에러 1033")
mo = regex.search(text)
if mo != None:
print(mo.group())
- 정규 표현식은 단순한 리터럴 문자열을 검색하는 것보다 훨씬 많은 기능들을 제공하는데, 즉 특정 패턴의 문자열을 검색하는데 매우 유용하다.
- 그 한가지 예로 웹페이지나 텍스트에서 특정 패턴의 전화번호를 발췌하는 기능에 대해 알아보자.
- 전화번호의 패턴은
032-232-3245
와 같이3자리-3자리-4자리
로 구성되어 있다고 가정하자. - 정규식에서 숫자를 의미하는 기호로
\d
를 사용한다. - 여기서
d
는digit
을 의미하고0
~9
까지의 숫자 중 아무 숫자나 될 수 있다. - 따라서, 위 전화번호 패턴을 정규식으로 표현하면
\d\d\d-\d\d\d-\d\d\d\d
와 같이 될 수 있다. - 아래는 이러한 패턴을 사용하여 전화번호를 발췌하는 예이다.
import re
text = "문의사항이 있으면 032-232-3245 으로 연락주시기 바랍니다."
regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
matchobj = regex.search(text)
phonenumber = matchobj.group()
print(phonenumber)
- 위 예제에서
re.compile(전화번호패턴)
함수는 전화번호 패턴에 갖는 정규식 객체를 리턴하게 되고,search()
를 사용하여 첫번째 전화번호 패턴에 매칭되는 번호를 리턴한다. - 그리고 이로부터 실제 전화번호를 얻기 위해서는
group()
메서드를 사용하였다.
- 위의 전화번호 예제에서는 숫자를 표현하는
\d
만을 살펴보았는데, 정규 표현식에는 매우 다양한 문법과 기능들이 제공되고 있다. - 아래는 이러한 다양한 정규식 표현 중 자주 사용되는 패턴들을 정리한 것이다.
패턴 | 설명 | 예제 |
---|---|---|
^ |
이 패턴으로 시작해야 함 |
^abc : abc 로 시작해야 함 ( abcd , abc12 등) |
$ |
이 패턴으로 종료되어야 함 |
xyz$ : xyz 로 종료되어야 함 ( 123xyz , strxyz 등) |
[문자들] |
문자들 중에 하나이어야 함. 가능한 문자들의 집합을 정의함. |
[Pp]ython : Python 혹은 python
|
[^문자들] |
[문자들] 의 반대로 피해야할 문자들의 집합을 정의함. |
[^aeiou] : 소문자 모음이 아닌 문자들 |
` | ` | 두 패턴 중 하나이어야 함 (OR 기능) |
? |
앞 패턴이 없거나 하나이어야 함 (Optional 패턴을 정의할 때 사용) |
\d? : 숫자가 하나 있거나 없어야 함 |
+ |
앞 패턴이 하나 이상이어야 함 |
\d+ : 숫자가 하나 이상이어야 함 |
* |
앞 패턴이 0개 이상이어야 함 |
\d* : 숫자가 없거나 하나 이상이어야 함 |
패턴{n} |
앞 패턴이 n 번 반복해서 나타나는 경우 |
\d{3} : 숫자가 3개 있어야 함 |
패턴{n, m} |
앞 패턴이 최소 n 번, 최대 m 번 반복해서 나타나는 경우 ( n 또는 m 은 생략 가능) |
\d{3,5} : 숫자가 3개, 4개 혹은 5개 있어야 함 |
\d |
숫자 0 ~ 9 |
\d\d\d : 0 ~ 9 범위의 숫자가 3개를 의미 ( 123 , 000 등) |
\w |
문자를 의미 |
\w\w\w : 문자가 3개를 의미 ( xyz , ABC 등) |
\s |
화이트 스페이스를 의미하는데, [\t\n\r\f] 와 동일 |
\s\s : 화이트 스페이스 문자 2개 의미 ( \r\n , \t\t 등) |
. |
뉴라인( \n ) 을 제외한 모든 문자를 의미 |
.{3} : 문자 3개 ( F15 , 0x0 등) |
- 정규식 패턴의 한 예로
에러 {에러번호}
와 같은 형식을 띄는 부분을 발췌해 내는 예제를 살펴보자. - 여기서 에러 패턴은
에러
라는 리터럴 문자열과 공백 하나, 그 뒤에 1개 이상의 숫자이다. - 이를 표현하면 아래와 같다.
import re
text = "에러 1122 : 레퍼런스 오류\n 에러 1033: 아규먼트 오류"
regex = re.compile("에러\s\d+")
mc = regex.findall(text)
print(mc)
# 출력: ['에러 1122', '에러 1033']
- 위 예제는 첫번째 패턴 매칭값을 리턴하는
search()
메서드 대신 패턴에 매칭되는 모든 결과를 리턴하는findall()
을 사용하였다. -
findall()
는 결과 문자열들의 리스트(list
)를 리턴한다.
- 정규 표현식에서
( )
괄호는 그룹을 의미한다. - 예를 들어, 전화번호의 패턴을
\d{3}-\d{3}-\d{4}
와 같이 표현하였을 때, 지역번호 3자를그룹1
으로 하고 나머지 7자리를그룹2
로 분리하고 싶을 때,(\d{3})-(\d{3}-\d{4})
와 같이 둥근 괄호로 묶어 두 그룹으로 분리할 수 있다. - 이렇게 분리된 그룹들은
MatchObject
의group()
메서드에서 그룹 번호를 파라미터로 넣어 값을 가져올 수 있다. - 첫번째 그룹 지역번호는
group(1)
으로, 두번째 그룹은group(2)
와 같이 사용한다. - 그리고 전체 전화번호를 가져올 때는
group()
혹은group(0)
을 사용한다.
import re
text = "문의사항이 있으면 032-232-3245 으로 연락주시기 바랍니다."
regex = re.compile(r'(\d{3})-(\d{3}-\d{4})')
matchobj = regex.search(text)
areaCode = matchobj.group(1)
num = matchobj.group(2)
fullNum = matchobj.group()
print(areaCode, num) # 032 232-3245
- 그룹을 위와 같이 숫자로 인덱싱하는 대신 그룹이름을 지정할 수도 있는데 이를 정규식에서 Named Capturing Group 이라 한다.
- 파이썬에서 Named Capturing Group을 사용하는 방법은
(?P<그룹명>정규식)
와 같이 정규식 표현 앞에?P<그룹명>
을 쓰면 된다. - 그리고 이후
MatchObject
에서group('그룹명')
을 호출하면 캡쳐된 그룹 값을 얻을 수 있다.
import re
text = "문의사항이 있으면 032-232-3245 으로 연락주시기 바랍니다."
regex = re.compile(r'(?P<area>\d{3})-(?P<num>\d{3}-\d{4})')
matchobj = regex.search(text)
areaCode = matchobj.group("area")
num = matchobj.group("num")
print(areaCode, num) # 032 232-3245