Language/Python Python 정규 표현식 심화
  • 728x90
    반응형

     

     

    목차

       

       

      메타 문자

      +, *, [], {}등의 메타문자는 매치가 진행될 때 현재 매치되고 있는 문자열의 위치가 변경된다. (보통 소모된다고 표현) 하지만 이와 달리 문자열을 소모가 없는(zero-width assertions) 메타 문자들에 대해서 살펴본다.

       

       

      🎯 |

       

      |메타 문자는 "or"의 의미와 동일하다. A|B라는 정규식이 있다면 이것은 A또는 B라는 의미가 된다.

       

       

       

      🎯 ^

       

      ^메타문자는 문자열의 맨 처음과 일치함을 의미한다. 컴파일 옵션 re.MULTILINE을 사용할 경우에는 여러줄의 문자열에서는 각 라인의 처음과 일치하게 된다.

       

       

      ^Life정규식은 "Life"라는 문자열이 처음에 온 경우에는 매치하지만 처음 위치가 아닌 경우에는 매치되지 않음을 알 수 있다.

       

       

      🎯 $

       

      $메타문자는 ^메타문자의 반대의 경우이다. $는 문자열의 끝과 매치함을 의미한다.

       

       

      short$정규식은 검색할 문자열의 "short"로 끝난 경우에는 매치되지만 그 이외의 경우에는 매치되지 않음을 알 수 있다.

      💡 ^ 또는 $ 문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 [^], [$] 처럼 사용하거나 \^, \$ 로 사용하면 된다.

       

      🎯 \A

       

      \A는 문자열의 처음과 매치됨을 의미한다. ^와 동일한 의미지만 re.MUTILINE옵션을 사용할 경우에는 다르게 해석된다.

      re.MULTILINE옵션을 사용할 경우 ^은 라인별 문자열의 처음과 매치되지만 \A는 라인과 상관없이 전체 문자열의 처음하고만 매치된다.

       

       

      🎯 \Z

       

      \Z는 문자열의 끝과 매치됨을 의미한다. 이것 역시 \A와 동일하게 re.MUTILINE옵션을 사용할 경우 $메타문자와는 달리 전체 문자열의 끝과 매치된다.

       

       

      🎯 \b

       

      \b는 단어 구분자(Word boundary)이다. 보통 단어는 whitespace에 의해 구분이 된다.

       

       

      \bclass\b정규식은 "class"라는 단어와 매치됨을 의미한다. no class at all의 class라는 단어와 매치됨을 확인할 수 있다.

       

       

      위 예의 the declassified algorithm 라는 문자열 안에 class라는 문자열이 포함되어 있긴 하지만 whitespace로 구분된 단어가 아니므로 매치되지 않는다.

       

       

      subclass 라는 문자열 역시 class앞에 sub라는 문자열이 더해져 있으므로 매치되지 않는다.

       

      \b메타문자를 이용할 경우 주의해야 할 점은 \b는 파이썬 리터럴 규칙에 의하면 백스페이스(Back Space)를 의미하므로 백스페이스가 아닌 Word Boundary임을 알려주기 위해 r'\bclass\b'처럼 raw string 임을 알려주는 기호 r을 반드시 붙여주어야 한다.

       

       

      🎯 \B

       

      \B메타문자는 \B메타문자의 반대의 경우이다. 즉, whitespace로 구분된 단어가 아닌 경우에만 매치된다.

       

       

      class라는 문자열 좌우에 whitespace 가 있는 경우에는 매치가 안되는 것을 확인할 수 있다.

       

       

       

       

      그룹핑

      📌 ABC라는 문자열이 계속해서 반볻되는지 조사하는 정규식을 작성하고 싶다고 하자. 이럴 때 필요한 것이 바로 그룹핑(Grouping)이다.

       

      (ABC)+

       

      그룹을 만들어 주는 메타문자는 바로 ()이다.

       

       

      🔔 예)

       

      \w+\s+wd+[-]\d+[-]\d+이름 + " " + 전화번호형태의 문자열을 찾는 정규효현식이다. 이렇게 매치된 문자열 중에서 이름만 뽑아내고 싶다면 어떻게 해야 할까?

       

      위 예에서 "이름" 부분만을 뽑아내려 한다면 다음과 같이 할 수 있다.

       

       

      이름에 해당되는 \w+부분을 그룹((\w+))으로 만들면 match object의 group(index) 메서드를 이용하여 그룹핑된 부분의 문자열만 뽑아낼 수 있다. group 메쏘드의 index는 다음과 같은 의미를 갖는다.

       

      group(인덱스) 설명
      group(0) 매치된 전체 문자열
      group(1) 첫 번째 그룹에 해당되는 문자열
      group(2) 두 번째 그룹에 해당되는 문자열
      group(n) n 번째 그룹에 해당되는 문자열

       

      🔔 예)

       

      이번에는 전화번호 부분을 추가로 그룹((\d+[-]\d+[-]\d+))으로 만들었다. 이렇게 하면 group(2) 처럼 사용하여 전화번호만을 뽑아낼 수 있다.

       

      만약 전화번호 중에서 국번만 뽑아내고 싶으면 다음과 같이 국번 부분을 그룹핑하면 된다.

       

       

      위 예에서 보듯이 (\w+)\s+((\d+)[-]\d+[-]\d+)처럼 그룹을 중첩되게 사용하는 것도 가능하다. 그룹이 중첩되어 사용되는 경우는 바깥쪽부터 시작하여 안쪽으로 들어갈수록 인덱스가 증가한다.

       

      🎯 그룹핑된 문자열 재참조하기

      그룹의 또 하나 좋은 점은 한번 그룹핑된 문자열을 재참조(Backreferences)할 수 있다는 점이다.

       

       

      정규식 (\b\w)\s+\1(그룹1) + " " + "그룹1과 동일한 단어"와 매치됨을 의미한다. 이렇게 정규식을 만들게 되면 2개의 동일한 단어가 연속적으로 사용되어야만 매치되게 된다. 재 참조 메타문자인 \1은 정규식의 그룹중 첫번쨰 그룹을 지칭하다.

       

      정규식은 그룹을 만들 때 그룹명을 지정할 수 있게 되어 있다. 그 방법은 다음과 같다.

      (?P<name>\w+)\s+((\d+)[-]\d\+[-]\d+)

       

      위 정규식은 이전에 보았던 이름과 전화번호를 추출하는 정규식이다. 기존과 달라진 부분은 다음과 같다.

      (\w+) → (?P<name?\w+)

       

      (\w+)라는 그룹에 "name" 이라는 이름을 붙인 것에 불과하다. 여기서 사용한 (?. . .)표현식은 정규표현식의 확장구문이다. (여기서. . .은 다양하게 변한다는 anything의 의미이다.)

       

      그룹에 이름을 지어주기 위해서는 다음과 같은 확장구문을 사용해야 한다.

      (?P<그룹명>...)

       

      🔔 예) 그룹에 이름을 지정하고 참조

       

      그룹명을 이용하면 정규식 내에서 재참조하는 것도 가능하다.

       

       

      위 예에서 보듯이 재 참조시에는 (?P=그룹명)이라는 확장구문을 이용해야 한다.

       

       

       

      잔방 탐색

      📌 전방 탐색(Lookahead Assertions) 확장 구문.

       

      🔔 예)

       

      정규식 .+:과 일치하는 문자열로 "http:" 가 리턴되었다. 하지만 "http:"라는 검색 결과에서 ":" 을 제외하고 출력하려면 어떻게 해야 할까? 위 예는 그나마 간단하지만 그룹핑은 추가로 할 수 없다는 조건까지 더해진다면 어떻게 해야 할까?

       

      • 긍정형 전방 탐색 ( (?=. . .) ) - . . . 에 햐당되는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소모되지 않는다.
      • 부정형 전방 탐색 ( (?!. . .) ) - . . . 에 해당되는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소모되지 않는다.

       

      🎯 긍정형 전방 탐색

      긍정형 전방 탐색을 이용하면 http: 의 결과를 http로 바꿀 수 있다.

       

       

      정규식 중 :에 해당하는 부분이 긍정형 전방탐색 기법이 적용되어 (?=:)으로 변경되었다. 기존 정규식과 검색에서는 동일한 효과를 발휘하지만 :에 해당되는 문자열이 정규식 엔진에 의해 소모되지 않아(검색에는 포함되면 검색 결과에는 제외됨) 검색 결과에서는 :이 제거된 후 리턴되는 효과가 있다.

       

      정규식에 확장자가 "bat 인 파일은 제외해야 한다"는 조건을 추가해 보자.

      .*[,]([^b].?.?|.[^a]?.?|..?[^t]?)$

       

       

      🎯 부정형 전방 탐색

      위 케이스는 부정형 전방탐색을 사용하면 다음과 같이 간단하게 처리된다.

      .*[.](?!bat$).*$

       

      확장자가 bat가 아닌 경우에만 통과된다는 의미이다. bat라는 문자열이 있는지 조사하는 과정에서 문자열이 소모되지 않으므로 bat가 아니라고 판단되면 그 이후 정규식 매칭이 진행된다.

       

      exe 역시 제외 하라는 조건이 추가되더라도 다음과 같이 간단히 표현할 수 있다.

      .*[.](?!bat$|exe$).*$

       

       

       

      문자열 바꾸기

      📌 sub 메서드를 이용하면 정규식과 매치되는 부분을 다른 문자로 쉽게 바꿀 수 있다.

       

      🔔 예)

       

      sub 메서드의 첫 번쨰 입력 인수는 "바꿀 문자열(replacement)"이 되고, 두 번째 입력 인수는 "대상 문자열"이 된다. 위 예에서 볼 수 있듯이 blue 또는 white 또는 red라는 문자열이 colour라는 문자열로 바뀌는 것을 확인할 수 있다.

       

      바꾸기 횟수를 제어하려면 다음과 같이 세 번째 입력 인수로 count 값을 넘기면 된다.

       

       

      🎯 sub 메서드와 유사한 subn 메서드

      subn 역시 sub와 동일한 기능을 하지만 리턴되는 결과를 튜플로 리턴한다는 차이가 있다. 리턴된 튜플의 첫 번째 요소는 변경된 문자열이고, 두 번째 요소는 바꾸기가 발생한 횟수이다.

       

       

       

      🎯 sub 메서드 사용 시 참조 구문 사용하기

      sub 메서드를 사용할 때 참조 구문을 사용할 수 있다.

       

       

      위 예는 이름 + 전화번호의 문자열을 전화번호 + 이름으로 바꾸는 예이다. sub의 바꿀 문자열 부분에 \g<그룹명>을 이용하면 정규식의 그룹명을 참조할 수 있게된다.

       

      다음과 같이 그룹명 대신 참조번호를 이용해도 같은 결과가 리턴된다.

       

       

      🎯 sub 메서드의 입력 인수로 함수 넣기

      sub 메서드의 첫 번째 입력 인수로 함수를 넣을 수도 있다.

       

      🔔 예)

       

      hexrepl 함수는 match 객체(위에서 숫자에 매치되는)를 입력으로 받아 16진수로 변환하여 리턴하는 함수이다. sub의 첫 번째 입력 인수로 함수를 사용할 경우 해당 함수의 첫 번째 입력 인수에는 정규식과 매치된 match 객체가 입력된다. 그리고 매치되는 문자열은 함수의 리턴값으로 바뀌게 된다.

       

       

      Greedy vs Non-Greedy

      정규식에서 Greedy(탐욕스러운)란 어떤 의미일까?

       

       

      <.*>정규식의 매치결과로 <html>문자열이 리턴되기를 기대했을 것이다. 하지만 *메타문자는 매치할 수 있는 최대한의 문자열인 <html><head><title>Title</title>문자열을 모두 소모시켜 버렸다. <html>이라는 문자열까지만 소모되도록 막을 수 있는 방법에 대해 알아본다.

       

      다음과 같이 non-greedy 문자인 ?을 사용하면 *을 제한할 수 있다.

       

       

      non-greedy 문자인 ?*?, +?, ??, {m,n}?과 같이 사용할 수 있다. 가장 최소한의 반복을 수행하도록 도와주는 역할을 한다.

       

       

       

       

       

       

       

      728x90
      반응형

      'Language > Python' 카테고리의 다른 글

      Python 주소록 프로젝트  (0) 2017.07.25
      Python XML 처리  (0) 2017.07.25
      Python 정규 표현식 시작하기  (0) 2017.07.24
      Python 정규 표현식 살펴보기  (0) 2017.07.24
      [Python] 프로그래밍  (0) 2017.07.21
    상단으로