Skip to content

2.23. 내용정리: 26일차

흔한 찐따 edited this page May 23, 2022 · 2 revisions

1. 정규 표현식 (Regular Expression)

  • 정규 표현식은 특정한 규칙을 가진 문자열의 패턴을 표현하는 데 사용하는 표현식(Expression)으로 텍스트에서 특정 문자열을 검색하거나 치환할 때 흔히 사용된다.
  • 예를 들어, 웹 페이지에서 전화번호나 이메일 주소를 발췌한다거나 로그 파일에서 특정 에러 메시지가 들어간 라인들을 찾을 때 정규 표현식을 사용하면 쉽게 구현할 수 있다.
  • 정규 표현식은 간단히 정규식 혹은 Regex 등으로 불리우곤 한다.

2. 정규 표현식 사용

  • 정규식에서 가장 단순한 것은 특정 문자열을 직접 리터럴로 사용하여 해당 문자열을 검색하는 것이다.
  • 예를 들어, 로그 파일에 "에러 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()) 

3. 전화번호 발췌하기

  • 정규 표현식은 단순한 리터럴 문자열을 검색하는 것보다 훨씬 많은 기능들을 제공하는데, 즉 특정 패턴의 문자열을 검색하는데 매우 유용하다.
  • 그 한가지 예로 웹페이지나 텍스트에서 특정 패턴의 전화번호를 발췌하는 기능에 대해 알아보자.
  • 전화번호의 패턴은 032-232-3245 와 같이 3자리-3자리-4자리 로 구성되어 있다고 가정하자.
  • 정규식에서 숫자를 의미하는 기호로 \d 를 사용한다.
  • 여기서 ddigit 을 의미하고 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() 메서드를 사용하였다.

4. 다양한 정규식 패턴 표현

  • 위의 전화번호 예제에서는 숫자를 표현하는 \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 )를 리턴한다.

5. 정규식 그룹 (Group)

  • 정규 표현식에서 ( ) 괄호는 그룹을 의미한다.
  • 예를 들어, 전화번호의 패턴을 \d{3}-\d{3}-\d{4} 와 같이 표현하였을 때, 지역번호 3자를 그룹1 으로 하고 나머지 7자리를 그룹2 로 분리하고 싶을 때, (\d{3})-(\d{3}-\d{4}) 와 같이 둥근 괄호로 묶어 두 그룹으로 분리할 수 있다.
  • 이렇게 분리된 그룹들은 MatchObjectgroup() 메서드에서 그룹 번호를 파라미터로 넣어 값을 가져올 수 있다.
  • 첫번째 그룹 지역번호는 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