본문 바로가기
IT/엘리스 AI 트랙

[엘리스 AI 트랙 2기] 01주-정규표현식(06/22)

by 무녈 2021. 6. 27.

자료의 출처는 '엘리스 AI 트랙 2기' '(https://aitrack.elice.io/)' '리눅스 기초'강의이며, 학습 후 정리한 내용입니다.

⚡️올바르지 않은 내용이 있을 경우 댓글로 남겨주시면 감사하겠습니다.⚡️


정규표현식 

정규표현식

정규표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 도구로서 특정 프로그래밍 언어에 종속된 문법을 가진 것이 아니라 문자열을 처리하는 곳이라면 폭넓게 사용 가능한 도구

정규표현식을 배우는 이유

여러 프로그래밍 언어에서는 문자열과 관련된 라이브러리를 많이 제공한다.

그럼에도 불구하고 여러분이 정규표현식을 배워야 하는 이유는 무엇일까?

예시

보안을 위해 고객 정보 중 전화번호 가운데 자리의 숫자는 * 문자로 변경하세요.

고객 정보는 이름, 주민번호, 전화번호가 문자열 데이터로 주어진다고 가정

text = '''
Elice 123456-1234567 010-1234-5678
Cheshire 345678-678901 01098765432
'''

이 문제를 정규식을 사용하지 않고 풀려면 매우 복잡하게 풀어야 한다.

  1. 전체 텍스트를 공백 문자를 기준으로 나눈다.
  2. 나누어진 문자열이 전화번호 형식인지 점검한다.
  3. 전화번호를 다시 나누어 가운데 자리의 숫자를 *로 변환한다.
  4. 나눈 문자열을 다시 합쳐 전화번호를 완성한다.

그런데 나누어진 문자열을 전화번호 형식인지 어떻게 점검할까요? 그리고 가운데 자리의 숫자는 어떻게 나누어 *로 변환하고 다시 합칠까?

설상가상으로 전화번호의 구분문자인 -도 있기 때문에 문제 해결은 더욱 쉽지 않다.

그러나 정규표현식을 이용하면 매우 간편하게 코드를 작성할 수 있다.

 

앞서 예로 든 전화번호같이, 원하는 형식의 문자열을 검색할 때 메타문자와 수량자 등 다양한 패턴을 사용하여 매치하고, 그룹핑을 이용하여 원하는 부분만 골라내고 re모듈의 메서드로 문자열을 수정할 수도 있습니다.

re 모듈의 함수

앞선 실습에서 살펴보았듯이 re모듈의 함수는 정규식 객체에서 호출하여 사용할 수 있습니다.

그러나 앞으로의 실습에서는 정규식 객체 대신, 대부분 re모듈의 함수를 직접 사용합니다.

이제 re 모듈의 대표적인 함수들을 알아봅시다.

정규식 검사 함수

문자열에 대해 정규식으로 검사하는 함수는 대표적으로 re.match(), re.search(), re.findall(), re.finditer() 이렇게 4가지가 있습니다.

함수 이름 기능
re.match(pattern, string) string 시작 부분부터 패턴이 존재하는지 검사하여 MatchObject를 반환함.
re.search(pattern, string) string 전체에서 pattern이 존재하는지 검사하여 MatchObject를 반환함.
re.findall(pattern, string) string 전체에서 패턴과 매치되는 모든 경우를 찾아 list로 반환함.
re.finditer(pattern, string) string 전체에서 패턴과 일치하는 결과에 대한 iterater 객체를 반환함.

# iterater: 반복

문자열 수정 함수

re 모듈에는 패턴과 매치된 문자열을 찾아줄 뿐만 아니라, 편집할 수 있는 함수들도 존재합니다.

함수 이름 기능
re.sub(pattern, repl, string) string 에서 pattern과 매칭되는 부분을 repl로 수정한 문자열을 반환함.
re.subn(pattern, repl, string) re.sub()과 동일하지만, 함수의 결과를 (결과 문자열, 교체 횟수)꼴의 튜플로 반환함.

정규식 메타 문자

메타 문자

메타 문자: 특정한 문자 혹은 계열을 표현하는 약속된 기호

메타 문자를 이용하면 특정한 규칙을 가진 여러 단어를 짧게 압축할 수 있어 편리

메타 문자의미예시

메타문자 의미 예시
^ 문자열의 시작 ^www는 문자열의 맨 처음에 www가 오는 경우에 매치합니다.
$ 문자열의 끝 .com$은 문자열의 맨 끝에 .com이 오는 경우에 매치합니다.
| or 조건식 여러 가지 중 하나와 일치하면 매치합니다. `Apple
[] 문자 클래스 대괄호 [] 안에 들어있는 문자 중 하나라도 일치하면 매치합니다. [abc]는 ‘a,b,c’ 중 하나와 매칭됩니다.
\d 숫자를 나타냅니다.  
\D 숫자가 아닌 모든 문자를 나타냅니다.  
\w 알파벳 대소문자, 숫자, 밑줄(_)을 나타냅니다.  
\W \w에 해당되지 않는 문자들을 나타냅니다.  
\s 공백, 탭 문자와 매칭됩니다.  
\S \s에 매칭되지 않는 모든 문자를 나타냅니다.  
\n 개행 문자를 나타냅니다.  
\ 이스케이프용 문자. 특별한 의미를 나타내는 기호를 문자 그대로 나타내려고 할 때 사용합니다. 문자열 내에서 $문자를 찾기 위해서는 \$와 같이 나타내어야 합니다.
. 모든 문자와 대응되는 기호입니다.  

# ^ 메타 문자는 “문자열의 앞” 이란 의미였지만, [] 대괄호 안에서 쓰이면 not 의 의미

# 패턴이 대소문자를 구분하지 않게 하기 위해서는(?i) 플래그 사용

  이 플래그가 위치에 상관 없이 패턴 내에 포함되어있기만 하면, 해당 패턴은 대소문자를 무시

수량자(Quantifier)

수량자

동일한 글자나 패턴이 반복될 때, 그대로 정규표현식을 만들고자 하면 상당히 불편

\d와 \w를 이용하면 각각 숫자와 문자를 한 글자씩 매칭해주는데, 이어지는 문자를 패턴으로 만들어, 단어 단위로 매칭하고 싶을 때엔 상당히 불편

이런 상황에서 유용하게 사용할 수 있는 수량자

수량자 의미 예시
* 0개 이상 elice*는 “elic”, “elice”, “elicee”, “eliceee…eee”와 매칭됩니다.
+ 1개 이상 *와는 달리, 적어도 해당 문자가 1개 이상이어야 매칭됩니다.
? 0개 또는 1개 elice?는 “elic”, “elice”와 매칭됩니다.
{n} n개 \d{3}은 123, 456 등 세 자릿수와 매칭됩니다.
{n, m} n개 이상, m개 이하 \w{3, 5}는 알파벳 세 자리에서 다섯 자리의 단어와 매칭됩니다.
{n,} n개 이상 a{4,}는 a가 최소 4개 이상 연속된 경우에 매칭됩니다.

그룹

그룹

괄호는 그룹을 나타낸다.

그룹은 전체 패턴 내에서 하나로 묶여지는 패턴을 말한다.

그룹과 |를 결합한 형태, 또는 그룹 뒤에 수량자를 붙이는 패턴으로 자주 사용된다.

예시

  • (e|a)lice  elice, alice와 매칭
  • (tom|pot)ato는 tomato, potato와 매칭
  • (base|kick){2}  basebase, basekick, kickkick, kickbase 와 매칭

그룹의 재사용

한 번 만든 그룹은 재사용할 수도 있다.

만들어진 순서부터 1번부터 시작하는 그룹으로 참조할 수 있는데, 매치한 그룹을 패턴 내에서 재사용하려면 \\1과 같이 그룹 번호를 이스케이프하여 나타내야 한다.

예시

  • (to)ma\\1은 tomato와 매칭
  • 괄호를 사용하여 앞에서 만든 그룹 (to)를 뒤에서 재사용하는 모습

그 외

이외에도 그룹에는 re 모듈의 match 객체에 속해있는 group 메서드를 이용하여 매칭된 결과 중 일부 내용만을 추출할 수 있는 등 다양한 사용법이 있다.

sub 함수

sub()함수는 문자열에서 패턴에 일치하는 부분을 일정 문자열로 교체하여 반환하는 함수

print("m1 결과 : ", re.sub(p1, "\g<1>-****-\g<2>", text))

sub() 함수에서는 text에서 p1에 매칭하는 부분, 즉 전화번호 부분에서 그룹으로 저장한 앞 세 자리와 뒤 네 자리는 그룹을 참조하여 기존 값 그대로 유지하고, 가운데 자리를 ****로 대체

sub의 두 번째 인자는 대체할 문자열을 나타낸다.

대체할 문자열에서의 \g<1>, \g<2> 의 의미는

\g<그룹번호>의 형식으로 sub 함수의 대체할 문자열에서 정규식의 그룹을 참조할 수 있습니다.

비캡쳐링 그룹

potato, tomato두 단어를 보시면, 뒤에 세 글자 ato가 겹치는 것을 알 수 있습니다.

따라서 (pot|tom)ato 패턴은 tomato, potato두 단어 모두 매칭됩니다.

그러나 문자열 "tomato potato"에 "(pot|tom)ato"를 매칭한 결과를 출력해보면 ['pot', 'tom']가 출력됩니다.

그 이유는 파이썬 정규표현식에서 그룹으로 캡쳐한 부분이 있다면 이외의 부분들은 출력하지 않기 때문입니다. 위 패턴에서 ato는 그룹에 속해있지 않기 때문에 findall 함수에서 누락된 것입니다.

비캡쳐링 그룹 활용하기

이러한 문제를 해결하기 위한 방법 중 하나로 비캡쳐링 그룹이 있습니다.

비캡쳐링 그룹은 패턴 문자들을 묶되, 그룹 단위로 매칭되지는 않게끔 해줍니다. 그룹으로 묶은 것들을 최종 결과에서 따로 구분하여 사용하지 않는 경우에 적용합니다.

비캡쳐링 그룹을 만드는 방법은 (?:) 입니다.

 


참고

 

re — Regular expression operations — Python 3.9.5 documentation

This module provides regular expression matching operations similar to those found in Perl. Both patterns and strings to be searched can be Unicode strings (str) as well as 8-bit strings (bytes). However, Unicode strings and 8-bit strings cannot be mixed:

docs.python.org

 

반응형

댓글