[첫화면으로]Perl/문자열나누기

마지막으로 [b]

1. 하나의 스트링을 쪼개어서 배열로 만들기
1.1. split에 lookaround를 사용하여 특수한 경우가 생기지 않게 하라
1.2. split의 특별한 케이스
2. 하나의 스트링을 인코딩에 관계없이 일정 갯수의 문자로 나누기

1. 하나의 스트링을 쪼개어서 배열로 만들기

@변수 = split( / 구분자 /, $변수);

@변수 = split( / (구분자) /, $변수);

1.1. split에 lookaround를 사용하여 특수한 경우가 생기지 않게 하라

구분자가 단일 캐릭터일 때. 쉽다.
use v5.10;

my @letters = split /:/, 'a:b:c:d:e';
say join(":", @letters);
a:b:c:d:e

구분자 패턴의 길이가 2이상이거나 가변적일 때도, 무난하다.
my @cats = split /\s+/, 'Buster
        Mimi     Roscoe';
say join(":", @cats);
Buster:Mimi:Roscoe

구분자가 좌우대칭인 형태, 즉 각 값의 시작과 끝을 표시하는 마크가 있는 상황이라면 좀 복잡해진다. 값들 사이에 있는 패턴을 가지고 쪼갤 경우 첫번째 값의 앞과 마지막 값의 뒤에 있는 게 지워지지 않는다.

my @cats = split /></, '<Buster><Mimi><Roscoe>';
say join(":", @cats);
<Buster:Mimi:Roscoe>

이렇게 남은 것은 split한 후에 따로 처리하고자 할 수도 있다:
my @cats = split /></, '<Buster><Mimi><Roscoe>';
$cats[0] =~ s/<//;
$cats[-1] =~ s/>//;
say join(":", @cats);

물론 이렇게 해도 동작은 하지만, 이렇게 따로 처리해야 하는 특별한 경우를 만들지 않는 것이 좋다.

제거하려는 캐릭터들 모두에 매치되도록 구분자를 지정할 경우, 한가지 문제가 생긴다. 제일 앞에 있는 구분자 앞쪽에 빈 스트링 하나가 첫번째 원소가 된다: (출력 부분에 buster 앞에 콜론이 붙은 것에 유의)
my @cats = split /><|\A<|>\z/, '<buster><mimi><roscoe>';
say join(":", @cats);
:buster:mimi:roscoe

첫번째 필드를 쉬프트시켜서 제거할 수 있으나, 역시 보기 좋지 않다:
my @cats = split /><|\A<|>\z/, '<buster><mimi><roscoe>';
shift @cats;

say join(":", @cats);

캐릭터들을 매치시키는 게 아니라, lookaround를 사용하여 이런 좌우대칭 형태 구분자의 사이에서 split을 하게 할 수 있다. lookaround는 스트링 내의 특정한 조건에 매치가 되며 캐릭터를 소모하지 않는다1.

lookbehind와 lookahead2를 나란히 사용하여, 두 조건이 동시에 매치되는 지점에서 스트링을 쪼갤 수 있다.

my @cats = split /(?<=>)(?=<)/, '<buster><mimi><roscoe>';
say join(":", @cats);
<buster>:<mimi>:<roscoe>
위 출력결과에서는, 구분자 캐릭터들이 없어지지는 않았으나, 이제 각 원소들을 동일하게 처리하면 되며 특별히 처리할 케이스가 없다.

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);
buster:mimi:roscoe

좀 더 복잡하게, 각 필드가 따옴표로 둘러쌓여 있고, 다시 쉼표로 구분되어 있는 경우:
my @cats =
    map { my $s = $_; $s =~ s/\A"|"\z//g; $s }
        split /(?<="),(?=")/,
        '"Buster","Mimi","Roscoe"';
say join(":", @cats);

쉼표와 따옴표를 한번에 제거할 수 있는 더 복잡한 정규표현식을 만들 수도 있겠지만, 지금처럼 단순한 단계 두 번을 거치는 것에 비해, 코드를 읽고 유지하기에 어려울 것이다.

요점:

1.2. split의 특별한 케이스

split 에 구분자로 주어지는 패턴이 다음과 같은 경우는 특별하게 동작한다:

my $str = '  Buster and Mimi';

print join(":", split //, $str);     # 빈 패턴
#  : :B:u:s:t:e:r: :a:n:d: :M:i:m:i

print join(":", split /z*/, $str);   # 빈 스트링
#  : :B:u:s:t:e:r: :a:n:d: :M:i:m:i

print join(":", split / /, $str);    # 일반적인 공백 패턴
# ::Buster:and:Mimi                  # 앞에 ""가 두 번 들어감
print join(":", split ' ', $str);    # 따옴표로 둘러싼 단일 공백의 경우
# Buster:and:Mimi                    # 앞에 두 개의 ""가 없어짐

$str = "Line 1\nLine 2\nLine 3";
print join(":", split /^/, $str);
# Line 1
# :Line 2
# :Line 3

2. 하나의 스트링을 인코딩에 관계없이 일정 갯수의 문자로 나누기

2007년에 끙끙댔던 문제인데, 막상 답을 알고 나니 매우 간단해져서, 과거 내용은 삭제함

# 이 파일은 cp949로 저장되어 있음
use Encode;

my $str    = decode("cp949", "English와 한글");
my $length = length($str);

for (my $i = 1; $i < $length; $i++) {
    printf "%2d : ", $i;
    print "[",
             encode("cp949", substr($str, 0, $i)),
             "] [",
             encode("cp949", substr($str, $i)),
             "]\n";
}

 1 : [E] [nglish와 한글]
 2 : [En] [glish와 한글]
 3 : [Eng] [lish와 한글]
 4 : [Engl] [ish와 한글]
 5 : [Engli] [sh와 한글]
 6 : [Englis] [h와 한글]
 7 : [English] [와 한글]
 8 : [English와] [ 한글]
 9 : [English와 ] [한글]
10 : [English와 한] [글]

기타

이름:  
Homepage:
내용:
 


컴퓨터분류

각주:
1. 즉 현재 검사하는 위치를 가리키는 포인터가 이동하지 않고 그 자리에 유지된다
2. 더 자세한 것은 /정규표현식 참조

마지막 편집일: 2012-2-11 12:25 am (변경사항 [d])
10657 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기