이 글에서는 이전 글에서 다루지 않았던 내용인 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해줍니다.