vim 정규식 수량 한정자

vim 정규식에서 non-greedy하게 텍스트를 선택하는 방법

vim 정규식 수량 한정자
Photo by Clay Banks / Unsplash

정규식 패턴 중 a는 a를 0번 이상 매치하는 '0개 이상'을 의미하는 수량 한정자입니다. 그런데 기본적으로 * 는 조건을 만족하는 가능한 많은 문자에 매칭됩니다. 한 쪽에서는 이러한 특성을 탐욕적(greedy)이라고 표현하기도 합니다. 보통은 a와 같은 특정한 글자보다는 다른 선택자나 그룹을 사용하는 경우가 더 많은데, 적절한 범위 내에서 원하는 부분만 정확하게 선택하려는 경우에는 이 탐욕적인 특성이 상당히 걸림돌로 작용하는 경우가 많습니다.

수량 한정자가 '가능한 적게' 매치하도록 하기 위해서는 뒤에 ?을 붙입니다.

# 원문
color colour coloor colooour

# /colo.*r/
# .은 임의의 문자에 해당하고, 가장 많은 조건을 매치하려하기 때문에
# => |color colour coloor colooour| 와 매칭합니다. 

# /color.*?r/
# 가장 적은 조건에 매칭하기 때문에
# => |color| 와 매칭합니다. 

이는 현재 대다수의 정규식 문법에서 지원하는 방식이지만, vim에서는 이 문법을 지원하지 않습니다. vim 정규식에서 ?는 "0 or 1"의 의미만 존재합니다.

그러나 이는 vim 정규식에서 해당 문법만 없다 뿐이지 기능은 존재합니다. 본래 정규식의 수량 한정자에는 {n, m}을 사용하여 n ~ m 의 글자수 범위를 지정하는 기능이 있습니다. vim에서는 이 때 \{-n, m}을 사용하여 해당 범위 내에서 가능한 작은 수에 매칭하는 수량한정자를 지정할 수 있습니다. 독특하게 시작, 끝 범위를 모두 생략할 수 있습니다. 따라서, 다음과 같이 수량 한정자를 사용할 수 있습니다.

  • .*? -> .\{-}
  • .+? -> .\{-1,}

하는 김에 다른 차이점도 하나 더 짚어보겠습니다.

보통 검색이나 치환 명령을 사용할 때, vim에서는 기본적으로 라인 이내의 단위에서 범위를 잡고, 치환을 라인별로 반복하여 적용합니다. 그런데 경우에 따라서는 패턴 자체가 여러 라인에 걸치는 경우가 있습니다. 긴 HTML 문서에서 <article> ... </article> 요소가 여러 라인에 걸쳐 있을 때, 이 요소를 삭제하려면 어떻게 할까요? 바로 \ 패턴을 사용합니다. \_.\+는 개행을 포함한 임의의 문자의 연속을 의미합니다. 이를 수량한정자와 결합하여 다음과 같이 처리할 수 있습니다.

%s#<article>\_.\{-}<\/article>##

파이썬 등의 스크립트를 사용할 때에는 정규식을 적용하는 플래그를 통해, 여러 줄로 된 텍스트를 한 줄처럼 사용할 수 있습니다.

import re

pat = r'\<article.*?\<\/article>'
s = "...."
result = re.sub(pat, "", s, flags=re.S)