-17,10 +17,107 |
=== # split에 lookaround를 사용하여 특수한 경우가 생기지 않게 하라 === |
|
* 원문: [http://www.effectiveperlprogramming.com/blog/1411 Use lookarounds to split to avoid special cases | The Effective Perler] |
* 번역, 요약 |
* 원문의 테스트 코드를, 출력 결과를 알아보기 쉽도록 수정을 했음 |
|
|
구분자가 단일 캐릭터일 때. 쉽다. |
{{{#!vim perl |
use v5.10; |
|
my @letters = split /:/, 'a:b:c:d:e'; |
say join(":", @letters); |
}}} |
{{{#!vim |
a:b:c:d:e |
}}} |
|
구분자 패턴의 길이가 2이상이거나 가변적일 때도, 무난하다. |
{{{#!vim perl |
my @cats = split /\s+/, 'Buster |
Mimi Roscoe'; |
say join(":", @cats); |
}}} |
{{{#!vim |
Buster:Mimi:Roscoe |
}}} |
|
구분자가 좌우대칭인 형태, 즉 각 값의 시작과 끝을 표시하는 마크가 있는 상황이라면 좀 복잡해진다. 값들 사이에 있는 패턴을 가지고 쪼갤 경우 첫번째 값의 앞과 마지막 값의 뒤에 있는 게 지워지지 않는다. |
|
{{{#!vim perl |
my @cats = split /></, '<Buster><Mimi><Roscoe>'; |
say join(":", @cats); |
}}} |
{{{#!vim |
<Buster:Mimi:Roscoe> |
}}} |
|
이렇게 남은 것은 <code>split</code>한 후에 따로 처리하고자 할 수도 있다: |
{{{#!vim perl |
my @cats = split /></, '<Buster><Mimi><Roscoe>'; |
$cats[0] =~ s/<//; |
$cats[-1] =~ s/>//; |
say join(":", @cats); |
}}} |
|
물론 이렇게 해도 동작은 하지만, 이렇게 따로 처리해야 하는 특별한 경우를 만들지 않는 것이 좋다. |
|
제거하려는 캐릭터들 모두에 매치되도록 구분자를 지정할 경우, 한가지 문제가 생긴다. 제일 앞에 있는 구분자 앞쪽에 빈 스트링 하나가 첫번째 원소가 된다: (출력 부분에 <code>buster</code> 앞에 콜론이 붙은 것에 유의) |
{{{#!vim perl |
my @cats = split /><|\A<|>\z/, '<buster><mimi><roscoe>'; |
say join(":", @cats); |
}}} |
{{{#!vim |
:buster:mimi:roscoe |
}}} |
|
첫번째 필드를 쉬프트시켜서 제거할 수 있으나, 역시 보기 좋지 않다: |
{{{#!vim perl |
my @cats = split /><|\A<|>\z/, '<buster><mimi><roscoe>'; |
shift @cats; |
|
say join(":", @cats); |
}}} |
|
캐릭터들을 매치시키는 게 아니라, lookaround를 사용하여 이런 좌우대칭 형태 구분자의 사이에서 split을 하게 할 수 있다. lookaround는 스트링 내의 특정한 조건에 매치가 되며 캐릭터를 소모하지 않는다<footnote(즉 현재 검사하는 위치를 가리키는 포인터가 이동하지 않고 그 자리에 유지된다)>. |
|
lookbehind와 lookahead<footnote(더 자세한 것은 [[/정규표현식]] 참조)>를 나란히 사용하여, 두 조건이 동시에 매치되는 지점에서 스트링을 쪼갤 수 있다. |
|
{{{#!vim perl |
my @cats = split /(?<=>)(?=<)/, '<buster><mimi><roscoe>'; |
say join(":", @cats); |
}}} |
{{{#!vim |
<buster>:<mimi>:<roscoe> |
}}} |
위 출력결과에서는, 구분자 캐릭터들이 없어지지는 않았으나, 이제 각 원소들을 동일하게 처리하면 되며 특별히 처리할 케이스가 없다. |
|
{{{#!vim perl |
my @cats = |
# map { s/\A<|>\z//rg } # Perl 5.14 부터는 /r 옵션을 써서 이렇게도 가능 |
map { my $s = $_; $s =~ s/\A<|>\z//g; $s } |
split /(?<=>)(?=<)/, |
'<buster><mimi><roscoe>'; |
say join(":", @cats); |
}}} |
{{{#!vim |
buster:mimi:roscoe |
}}} |
|
좀 더 복잡하게, 각 필드가 따옴표로 둘러쌓여 있고, 다시 쉼표로 구분되어 있는 경우: |
{{{#!vim perl |
my @cats = |
map { my $s = $_; $s =~ s/\A"|"\z//g; $s } |
split /(?<="),(?=")/, |
'"Buster","Mimi","Roscoe"'; |
say join(":", @cats); |
}}} |
|
쉼표와 따옴표를 한번에 제거할 수 있는 더 복잡한 정규표현식을 만들 수도 있겠지만, 지금처럼 단순한 단계 두 번을 거치는 것에 비해, 코드를 읽고 유지하기에 어려울 것이다. |
|
요점: |
* 구분자를 한 번에 제거할 필요는 없다 |
* lookbehind와 lookahead를 나란히 사용하여 스트링 내에 특정한 위치를 지정할 수 있다 |
|
|
|