정규 표현식에서 실수하기 쉬운 백레퍼런스

Perl에서 다음의 코드를 테스트했는데 예상했던 결과인 header content footer가 나오지 않았다.

$a = "header block content end of block footer\n";
$a =~ s/(block)|end of \1//g;
print $a;

대신에 header content end of footer가 출렸됐다. 잘 보면 (block)이 전체 문자열에서 먼저 치환된 다음 end of block의 치환이 시도된 것으로 보인다. 하지만 block이 이미 없어져서 end of block은 존재하지 않는다.

위의 코드를 다음과 같이 바꾸면 header content footer가 출력된다.

$a = "header block content end of block footer";
$a =~ s/(block)|end of block//g;
print $a;

그렇다고 다음의 코드가 header x content x footer를 출력하는 것도 아니다.

$a = "header block content end of block footer";
$a =~ s/(block)|end of x/x/g;
print $a;

end of x는 치환되지 않고 header x content end of x footer이 출력된다.

반면에 |를 이용해서 각자 치환이 되지 않고 덩어리로 치환이 되는 경우는 괜찮다. 예를 들어 다음의 코드는 예상대로 ''

<style>...</style>'' 블럭을 제거한다.

$a =~ s#<(script|style).*?</\1>##sg;

|가 그룹 안에 있기 때문에 첫 번째 그룹과 \1이 개별적으로 치환되는 것이 아니고 같이 묶여서 치환되기 때문에 그렇다.

이 정도는 이해할 만하다. 그런데 문제는 언어마다 이런 식의 백레퍼런스를 다르게 구현했다는 거다. 예를 들어 자바스크립트를 보자.

a = 'header block content end of block footer'
a.replace(/(block)|end of \1/g, '')

위의 코드는 header content footer를 출력한다.

당연히 다음 코드도 똑같은 결과를 보인다.

a = 'header block content end of block footer'
a.replace(/(block)|end of block/g, '')

참고로 다음 코드는 Perl가 같은 header x content end of x footer를 출력한다.

a = 'header block content end of block footer'
a.replace(/(block)|end of x/g, 'x')

오늘의 교훈은 백레퍼런스를 사용할 때 앞서의 그룹이 문자열을 치환한다면 그 그룹을 백레퍼런스하지 말자. 적어도 Perl에서는 원하는 결과가 안 나온다.

참고문헌

이 칸을 비워 두세요.