이 포스팅은 Regular Expression 시리즈 12 편 중 9 번째 글 입니다.

  • Part 1 - 01: 개념잡기
  • Part 2 - 02: 간단한 메타문자
  • Part 3 - 03: re module
  • Part 4 - 04: match 객체의 메서드
  • Part 5 - 05: Compile Option(컴파일 옵션)
  • Part 6 - 06: 파이썬 백슬래시 문제
  • Part 7 - 07: 다양한 메타문자
  • Part 8 - 08: Grouping(그루핑)
  • Part 9 - This Post
  • Part 10 - 10: 문자열 바꾸기
  • Part 11 - 11: Greedy 와 Non-Greedy
  • Part 12 - 자주쓰이는 정규표현식 초급 정리 - 1
▼ 목록 보기

개요

Lookahead Assertions는 이해하기가 어렵다. 하지만 예시와 함께라면 어떤 경우에 이를 사용해야 하는지 알 수 있을 것이다.

>>> p = re.compile(".+:")
>>> m = p.search("http://google.com")
>>> print(m.group())
http:

정규식 .+:과 일치하는 문자열로 http:를 돌려주었다. 만약 http:라는 검색 결과에서 :을 제외하고 출력하려면 어떻게 해야 할까? 위 예는 그나마 간단하지만 훨씬 복잡한 정규식이어서 그루핑은 추가로 할 수 없다는 조건까지 더해진다면 어떻게 해야 할까?

이럴 때 사용할 수 있는 것이 바로 전방 탐색이다. 전방 탐색에는 긍정(Positive)과 부정(Negative)의 2종류가 있고 다음과 같이 표현한다.

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

긍정형 전방 탐색

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

>>> p = re.compile(".+(?=:)")
>>> m = p.search("http://google.com")
>>> print(m.group())
http

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

여기서 알 수 있는 사실은, Lookahead Assertions이 zero-width assertion이라는 사실이다. 실제 검색에 적용되기 위해서는 엔진이 이를 가지고 있어야 하는데 그렇지는 않고, 그저 있는지 확인만 해주는 역할이다. 이를 소비된다 라는 표현으로 적었다는 것은 앞 글에서 배웠다.

부정적 전방 탐색

자, 이번에는 다음 정규식을 보자.

.*[.].*$

이 정규식은 파일 이름 + . + 확장자를 나타내는 정규식이다. 이 정규식은 foo.bar, autoexec.bat, sendmail.cf 같은 형식의 파일과 매치될 것이다.

이 정규식에 확장자가 “bat인 파일은 제외해야 한다”는 조건을 추가해 보자. 가장 먼저 생각할 수 있는 정규식은 다음과 같다.

.*[.][^b].*$
이 정규식은 메타 문자를 사용하여 확장자의 첫 번째 문자가 b가 아니거나 두 번째 문자가 a가 아니거나 세 번째 문자가 t가 아닌 경우를 의미한다. 이 정규식에 의하여 foo.bar는 제외되지 않고 autoexec.bat은 제외되어 만족스러운 결과를 돌려준다. 하지만 이 정규식은 sendmail.cf처럼 확장자의 문자 개수가 2개인 케이스를 포함하지 못하는 오동작을 하기 시작한다.

따라서 다음과 같이 바꾸어야 한다.

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

확장자의 문자 개수가 2개여도 통과되는 정규식이 만들어졌다. 하지만 정규식은 점점 더 복잡해지고 이해하기 어려워진다.

그런데 여기에서 bat 파일말고 exe 파일도 제외하라는 조건이 추가로 생긴다면 어떻게 될까? 이 모든 조건을 만족하는 정규식을 구현하려면 패턴은 더욱더 복잡해질 것이다. 이런 상황에서 우리는 부정적 전방 탐색을 사용한다.

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

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

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

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

Reference

07-2 정규 표현식 시작하기