참조1 참조2
데이타 bc bd abc abd abf
정규표현식 (a)?b(?(1)c|d)
perl 에서 이렇게 돌려봤다.
use 5.010;
@data = qw(bc bd abc abd abf);
$regex = qr{(a)?b(?(1)c|d)};
for(@data) {
say $_ if /$regex/;
}
결과는
bd
abc
abd
bd와 abc는 예상한 대로 였는데 abd가 예상밖이었다.
펄 최신판(5.20.x)을 쓰고 있는데 혹시 버전업이 되면서 버그가 생긴건가 싶어서 우분투에 깔려있는 5.18.x 의 펄로 실행해봐도 결과는 같았다. 조건표현식에서 진도가 딱 멈췄다. if else 는 되는데 if then은 왜 안되는걸까?
글의 첫머리에 링크해놓은 곳에서 그것에 대한 설명을 발견했다.
먼저 $_ 로 출력할 것이 아니라 $&(매치된 부분만 출력)로 say문장을 바꿔서 실행해보면 다음과 같다. 기호(&)가 요상하게 표시가 되는데 숫자 7 위에 있는 기호이다.
say $& if /$regex/;
결과는
bd
abc
bd
abd 전체가 매치된 것이 아니라 bd만 매치된 것 이었는데 $_로 출력을 하니까 전체문장인 abd가 보였던 것이다.
조건표현식은 다음과 같은 구조를 갖는다
(조건표현)?
- 여기서 ? 는 수량자이다. 없거나 하나 있거나. (a)? 는 a가 있거나 없거나를 말한다.
- (조건표현)?문장
- 공통부분. (a)?b 는 a가 있으면 ab 가 되고 a가 없으면 b가 된다. b는 언제나 포함되니까 공통부분이다.
- (조건표현)?문장(?(캡쳐그룹))
- (?(캡쳐그룹))는 캡쳐그룹이 있는가 없는가를 나타낸다. 물음표바깥에도 안쪽에도 괄호가 있다. 조건정규표현식의 문법이다. (a)?b(?(1)) 에서 숫자1은 왼쪽에 있는 첫번째그룹인 (a)를 나타낸다. a가 있는가하고 물어보는 것이다. 그룹은 괄호로 친 부분을 나타내는 거니까 만약 (a)(b)(c)(?(3))하면 숫자3은 (c)를 나타낸다. (b)는 숫자2다. 캡쳐그룹이 많아져서 또는 다른 이유로 특정그룹의 지정을 문자로 할 수도 있다. 캡쳐그룹에 적당한 이름을 주고 조건문에서 그 이름을 쓰는 방법이다. 펄에서는 이렇게 한다.
- (a)? 를 (?<here>a) 이렇게 here라는 이름을 주고
- (?(1)) 를 (?(<here>)) 이렇게 here를 참조한다.
- (a)?b(?(1)c|d)
- (?<here>a)b(?(<here>)c|d)
- 물음표가 늘어나서 조금 복잡해보이지만 이해 못할 바는 아니다.
- (?<here>a)를 (?'here'a) 또는 (?P<here>a) 이렇게 할 수도 있다.
- (조건표현)?문장(?(캡쳐그룹)예|아니오)
- 조건이 참이면 [예] 거짓이면 [아니오]가 선택된다. 둘사이는 | 로 구분되어있다. [예]가 또는 [아니오]가 없을 수도있다. 즉 (조건표현)?문장(?(캡쳐그룹)예) 또는 (조건표현)?문장(?(캡쳐그룹)|아니오) 이렇게 쓸 수 있다.
- (a)?b(?(1)c|d)를 해석해보면 그룹1인 (a)가 있으면 c를 선택하고 (a)가 없으면 d를 선택한다는 뜻이다. b를 넣어서 전체적으로 보면 (a)가 있는 경우는 abc가 선택되고 (a)가 없는 경우는 bd 가 선택되는 것이다.
^(a)?b(?(1)c|d)
괄호바깥에 ^ 가 와야지 괄호안에 들어가 버리면 a 가 없을때 ^까지 같이 사라져 버리지만 이렇게 괄호바깥으로 빼버리면 a가 없어도 ^는 살아남아 ^b가 된다.
^(a?)b(?(1)c|d) 이렇게 수량자 물음표를 괄호안에 집어넣으면 어떻게 될까?
a가 없는경우는 ^()b(?(1)c|d)가 되고 a가 있는 경우는 ^(a)b(?(1)c|d) 가 된다. 둘다 캡쳐그룹은 살아있다. 즉 괄호는 살아있다. 앞의 경우는 공백으로 뒤의 경우는 a가 있는 것만이 다르지 캡쳐그룹자체는 여전히 있다. 그래서 ?(1)이 항상 참이 되어 c 만 선택된다. 그래서 abc와 bc만을 선택하게 된다. 주의하도록 하자.
덧글