지금까지 정규 표현식을 오해하고 있었던 이야기

May 20, 2017

TL;DR: /{\S*}/ -> {word}}의 결과를 아시는 분은 재끼셔도 좋습니다.

우선 답부터

답부터 이야기하자면 {word}}를 매칭합니다. 정규식을 배울 때 가장 처음에 배우는 내용(이라고 생각합니다) 중에 하나죠.

탐욕적인(Greedy) 패턴 매칭

한 문자, 또는 주어진 갯수만큼의 문자를 소비하면 되는 다른 정규식과는 달리, 조건에 맞는 문자를 한 개 이상(+) 또는 0개 이상(*)인 경우를 처리하는 경우에는 한가지 결정해야 할 부분이 있습니다. 그건 바로 조건에 맞는 경우가 한가지 이상일 때 이를 어떻게 다룰 것인가, 라는 부분인데요. 위에서 보였던 문제가 이에 해당합니다.

우선, {word}는 주어진 정규 표현식을 만족시킬 수 있습니다. 물론 첫 번째 }\S로 매칭한 뒤 두번째 }를 사용하여 {word}}를 매칭할 수도 있죠. 정규표현식에서는 이 두 가지 중에서 후자를 사용하며, 이를 탐욕적이라고 표현합니다.

좀 더 일반적인 설명을 해볼까요. 탐욕적이라는 것은 현재 선택 가능한 매칭 후보군에서 가장 큰 덩어리를 취한다는 의미입니다. 예제를 더 봐보죠.

사용한 정규표현식: /{\S*}/

"{word}}"
#=> 후보군: "{word}" or "{word}}"
#=> 결과: "{word}}"

"{word1}-{word2}"
#=> 후보군: "{word1}" or "{word1}-{word2}"
#=> 결과: "{word1}-{word2}"

"{word1{word2}}"
#=> 후보군: "{word1{word2}" or "{word1{word2}}"
#=> 결과: "{word1{word2}}"

각각 취할 수 있는 가장 큰 덩어리를 매칭하는 것을 확인할 수 있습니다.

당신 나태(Lazy)하시군요?

물론 문자열 하나에서 다중 치환을 해야하는 경우에는 여러가지 의미로 귀찮아지기 때문에, 정규표현식에서는 탐욕적이지 않은 방식도 제공하고 있습니다.

각각의 수량자 뒤에 ?를 사용하면, 현재 선택 가능한 매칭 후보군에서 가장 작은 덩어리를 취하게 됩니다. 탐욕적인 매칭과는 정반대로 동작하며 이를 나태(Lazy)한 방식이라고 표현합니다. 같은 예제를 사용하는 경우의 결과를 살펴보죠.

사용한 정규표현식: /{\S*?}/

"{word}}"
#=> 결과: "{word}"

"{word1}-{word2}"
#=> 결과: "{word1}"

"{word1{word2}}"
#=> 결과: "{word1{word1}"

간단하죠?

근데 이걸 몰랐어요?

네. 평범하게 앞에서부터 하나씩 문자를 소비해나가는 유한 오토마타라고 기억하고 있었기 때문입니다. 그래서 기본 소비가 나태하고, 옵션을 통해 탐욕적으로 소비하는 줄 알고 있었습니다…

결론

기초적인 사양도 가급적 꼼꼼히 읽읍시다.