우연히 Regex Tuesday라는 재미있는 사이트를 하나 발견했습니다. 매주 화요일에 정규식 문제를 올려두고 이를 바로 확인해 볼 수 있도록 만들어진 사이트입니다.
평소에 정규식에 관심도 많고 문제를 푸는 것도 재미있고 해서 시간나는 대로 이 사이트에 나온 문제에 대한 제 답과 해설을 달아볼 생각입니다. 순전히 제가 재미있자고 하는 일이지만 정규표현식을 공부하는 분들에게도 도움이 되었으면 하는 바람입니다.
1주차 문제는 다음과 같습니다. 왼쪽에 있는 문자열에 정규표현식 치환을 사용하여 오른쪽에 있는 결과물처럼 만믈면 됩니다.
| This is a test | This is a test | 
| This is is a test | This is <strong>is</strong> a test | 
| This test test is is | This test <strong>test</strong> is <strong>is</strong> | 
| This test is a test | This test is a test | 
| This this test is a test | This <strong>this</strong> test is a test | 
| cat dog dog cat dog | cat dog <strong>dog</strong> cat dog | 
| This test is a test tester | This test is a test tester | 
| hello world hello world | hello world hello world | 
| This nottest test is something | This nottest test is something | 
| This is IS a test | This is <strong>IS</strong> a test | 
| <Mack> I'll I'll be be back back in in a a bit bit. | <Mack> I'll <strong>I'll</strong> be <strong>be</strong> back <strong>back</strong> in <strong>in</strong> a <strong>a</strong> bit <strong>bit</strong>. | 
해당 사이트로 가면 정규식을 바로 작성해서 정답을 확인해 볼 수 있으므로 꼭 먼저 직접 풀어보시길 바랍니다. 당연한 얘기지만 제가 제시하는 답 외에도 다른 답이 존재할 수도 있으니, 어디까지나 참고용으로만 보셨으면 합니다. 🙂
[toggle txt_show="1주차 풀이 보기" txt_hide="1주차 풀이 감추기"]해답
정규식은 /\b([\w']+) (\1)\b/gi
치환식은 $1 <strong>$2</strong>
풀이
먼저 반복되는 단어를 찾아야 합니다.
단어 하나를 찾을 때는 흔히 \w+를 사용하는데 \w는 영문자 A-Z, a-z 숫자 0-9 특수문자 _를 포함하는 이스케이프 문자입니다. 따라서 \w+라고 입력하면 사실은 [A-Za-z0-9_]+를 입력한 것과 같은 뜻입니다. 문자 뒤의 +는 1개 또는 그 이상을 의미하는 수량자(quantifier)이므로 \w+은 "모든 문자가 A부터 Z까지 또는 a부터 z까지 또는 0부터 9까지 또는 _로 이루어진 연속되는 문자열"에 매칭됩니다. 예를 들어 "This is a test"라는 문장을 \w+로 찾아보면 "This", "is", "a", "test"가 매칭된다고 나옵니다. 그런데 마지막 패턴을 보면 I'll도 반복되는 단어에 포함시켰습니다. 작은따옴표는 \w에 포함되어 있지 않으므로 따로 패턴을 추가해주어야 합니다. 그래서 최종적으로 한 단어에 매칭하는 정규표현식은 [\w']+가 되었습니다.
저는 이와 같이 단어 패턴을 만들었지만 만약 이 문제에만 딱 맞는 답을 원한다면 \S+를 사용할 수도 있습니다(주의! 대문자 S입니다). \S는 '공백이 아닌' 문자에 매칭하는 이스케이프 문자입니다. 이 문제에는 공백 또는 단어 밖에 없으므로 \S로 단어를 매칭시킬 수 있습니다.
단어를 찾았으면 그 다음에는 이 단어가 반복되는 패턴을 만듭니다. 일단 단어 패턴을 괄호로 감쌉니다.
([\w']+)
괄호로 감싼 부분은 서브 패턴(sub pattern)이라고 부르는데 정규식을 실행하고 난 다음에 매칭된 부분을 찾을 때 편리합니다. 예를 들어, 자바스크립트에서 /([0-9]+)px/.test("The height is 300px") 와 같이 정규식을 실행하면 첫 번째 매칭된 부분을 저장하는 RegExp.$1에는 [0-9]+에 해당하는 값, 즉 "300"이 저장됩니다.
서브 패턴의 번호는 여는  괄호가 나온 순서대로 매겨집니다. 앞의 예에서 정규식을 /(([0-9]+)px)/ 로 바꾸면 괄호가 열린 순서에 의해 RegExp.$1에는 "300px"가, RegExp.$2에는 "300"이 저장됩니다. 조금 긴 정규식을 작성하다보면 간혹 괄호의 짝을 잘 못 맞추거나 순서를 헷갈리는 경우가 있으니 주의하셔야 합니다.
서브 패턴은 한 정규식 내에서도 사용할 수 있습니다. 예를 들어 첫 번째 패턴을 다시 불러오고 싶다면 \1과 같이 사용합니다. 백 레퍼런스(back reference)라 부르는 이러한 문법은 반복되는 문자열을 찾을 때 좋습니다. 바로 지금 우리가 사용할 것이죠. 이제 찾은 단어를 한 번 더 반복하는 패턴을 다음과 같이 검색할 수 있습니다. 단어와 단어 사이에는 한 칸의 공백이 있으므로 단어 문법과 백 레퍼런스 사이에도 공백을 하나 두겠습니다.
/([\w']+) \1/
나중에 치환할 때 사용하기 위해 백 레퍼런스도 서브 패턴으로 만들도록 하겠습니다.
/([\w']+) (\1)/
그런데 여기까지 해서 매칭되는 부분을 찾아보면 "This is a test" 중 "is is"에 매칭이 된다는 것을 알 수 있습니다.
"This is a test".match(/([\w']+) (\1)/)
첫 번째 서브 패턴은 한 단어의 일부가 아닌 전체에 매칭되어야 하므로 [\w']+ 앞에는 단어의 경계(\b)가 있도록 만들겠습니다. 단어 경계는 단어가 아닌 부분에 매칭되는 이스케이프 문자입니다. 단, 매칭 결과에서는 나타나지 않습니다. 예를 들어, "moon".match(/\bm/)을 실행하면 "moon"에 있는 "m"에 매칭이 되지만, "moon".match(/\bo/)를 실행하면 아무 것에도 매칭이 되지 않습니다. 단어 경계까지 포함시킨 패턴은 다음과 같습니다.
/\b([\w']+) (\1)\b/
다음으로는 변경자(modifier)를 추가하겠습니다. 변경자는 작성된 정규식 패턴의 성격을 바꾸어주는데, 자바스크립트 정규식에서는 닫는 슬래시(/) 뒤에 추가합니다. 우리가 추가할 변경자는 g(global, 전역 매칭)와 i(ignore case, 대소문자 구분안함) 입니다. 전역 매칭을 사용하지 않으면 정규식은 패턴에 일치하는 첫 번째 문자열만 찾고 종료되지만, 전역 매칭을 사용하면 패턴에 일치하는 모든 문자열을 찾습니다(표 3번째 문자열 참고). 대소문자 구분은 말 그대로 패턴 내에서 대소문자를 구분하지 않도록 합니다. 이 변경자를 사용해야 "This"를 참조한 백 레퍼런스가 "this"에도 매칭이 됩니다(표 5번째 문자열).
이제 매칭을 끝냈으면 치환할 차례입니다. 치환은 간단합니다. 치환 문자열 안에서 $1는 첫 번째 서브 패턴의 매칭 문자열로, $2는 두 번째 서브 패턴의 매칭 문자열로 바뀐다는 점만 기억하면 원하는 결과를 다음과 같이 얻을 수 있습니다.
$1 <strong>$2</strong>
예를 들어, "This this is a test"에서 $1는 "This", $2는 "this"가 됩니다.
[/toggle]
[adsense]