정규표현식 응용하기


이 글에서는 이전 글에서 다루지 않았던 내용인 lazy/greedy quantifier, lookahead, back reference에 대해 다루려고 합니다. 그리고 해당 기능을 이용한 예시를 보겠습니다.

게으른 수량자, 탐욕적 수량자

탐욕적 수량자는 패턴을 매칭할 때 되도록 큰 패턴을 찾으려고 하고 게으른 수량자는 반대로 가장 작은 패턴을 매치하려 합니다. 정규표현식은 기본적으로 탐욕적이기 때문에 "abc"abc"abc"같은 문자열을 아래 정규표현식으로 매치하면 전체("abc"abc"abc")를 다 캡쳐합니다. 하지만 +대신 +?를 사용하면 게으른 탐색을 하기 때문에 "abc"를 캡처합니다.

const re = /".+"/
re.exec('"abc"abc"abc"abc"')

이처럼 정규표현식의 반복 메타문자에 ?를 붙여주면 게으른 수량자로 작동합니다. ex) +?, *?, {n,m}?

그룹

앞선 글에서는 단순히 (...)로 쓰이는 캡처 그룹에 대해서만 알아봤습니다. 정규표현식에는 이 외에도 다양한 종류의 그룹이 존재하는데 이에 대해 알아보겠습니다.

Named capturing group

(?<name>...)을 사용하면 캡처한 그룹에 name을 부여합니다.

Non-capturing group

정규표현식을 쓸 때 단순히 grouping이 필요한 경우 캡처 그룹보다 비캡처 그룹을 사용하는것이 성능상 이득입니다. 비캡처 그룹은 (?:...)로 사용합니다.

Lookahead / Lookbehind

추가로 문자열을 다루다보면 특정 패턴 앞에 있는 문자, 혹은 특정 패턴이 앞에 없는 문자를 찾고싶은 경우가 있습니다. 이 때 사용하는 것이 lookahead와 lookbehind입니다. Lookahead와 lookbehind는 패턴을 찾는 방향에 따라 각각 positive/negative로 나뉩니다.

그룹 종류 문법
Named capturing group (?<name>...)
Non-capturing group (?:...)
positive lookahead (?=...)
negative lookahead (?!...)
positive lookbehind (?<=...)
negative lookbehind (?<!...)

역참조 (Back Reference)

정규표현식에서 (n은 자연수)는 n번째 캡처 그룹을 참조합니다. 비슷하게 k<name>은 named capture group을 참조합니다. 예를 들어 자바스크립트의 문자열을 찾는 정규표현식을 만드려면 "'로 감싸져있는 것을 찾아야 합니다. 그럼 아래와 같이 정규표현식을 작성할텐데 저 정규표현식은 "abc'와 같이 여는 따옴표와 닫는 따옴표가 다른 경우를 구분하지 못합니다.

// "abc'와 같은 문자열을 매칭한다.
/["'].+?["']/gm

이 때 역참조를 사용해서 아래와 같이 정규표현식을 작성하면 여는 따옴표와 동일한 닫힌 따옴표를 찾습니다.

/(["']).+?/gm

예제

위에서 언급했던 자바스크립트의 문자열을 매칭하는 정규표현식을 작성해봅시다. 자바스크립트 문자열은 ", ', `중 하나로 감싸져있고 내부에 로 escape된 따옴표는 무시합니다. 이를 적용하면 아래와 같이 작성할 수 있습니다.

/("|'|`).+?(?<!\)/

역참조와 negative lookbehind를 이용했고 가장 작은 단위를 찾아되기 때문에 +? 게으른 수량자를 사용했습니다.

두번째로 숫자가 주어졌을 때 3번째 자리마다 쉼표,를 추가하는 방법을 알아봅시다. 기존 문자열에 문자를 추가해야 되기 때문에 B 메타 문자로 적절한 위치를 찾은 뒤에 ,를 replace해주면 됩니다.

const re = /B(?=(?:d{3})+(?!d))/g
"1234567890".replace(re, ',')
//	1,234,567,890

Lookahead를 이용해 앞에 (3개의 숫자)가 1번 이상 반복되고 뒤에 숫자가 없는 문자 사이를 찾아서 replace해줍니다.