-
- 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는 스트링 내의 특정한 조건에 매치가 되며 캐릭터를 소모하지 않는다.
lookbehind와 lookahead를 나란히 사용하여, 두 조건이 동시에 매치되는 지점에서 스트링을 쪼갤 수 있다.
my @cats = 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);
buster:mimi:roscoe
좀 더 복잡하게, 각 필드가 따옴표로 둘러쌓여 있고, 다시 쉼표로 구분되어 있는 경우:
my @cats =
map { my $s = $_; $s =~ s/\A"|"\z//g; $s }
split /(?<="),(?=")/,
'"Buster","Mimi","Roscoe"';
say join(":", @cats);
쉼표와 따옴표를 한번에 제거할 수 있는 더 복잡한 정규표현식을 만들 수도 있겠지만, 지금처럼 단순한 단계 두 번을 거치는 것에 비해, 코드를 읽고 유지하기에 어려울 것이다.
요점:
- 구분자를 한 번에 제거할 필요는 없다
- lookbehind와 lookahead를 나란히 사용하여 스트링 내에 특정한 위치를 지정할 수 있다
1.2. split의 특별한 케이스
split 에 구분자로 주어지는 패턴이 다음과 같은 경우는 특별하게 동작한다:
- 빈 패턴
//
은 문자열을 캐릭터들로 쪼갠다.
- 빈 스트링, zero-width로 매치되는 패턴(
?
, *
, lookaround 등을 사용한)도 문자열을 캐릭터들로 쪼갠다.
- 따옴표로 둘러쌓인 단일 스페이스
' '
또는 " "
는 공백을 기준으로 문자열을 분리하되, 제일 앞에 있는 빈 필드는 버린다.
- 앵커
/^/
는 문자열을 라인 단위로 쪼갠다. (정확히 이 형태일 때만 그렇고, /^(?=.)/
와 같이 패턴에 다른 게 섞인다면 - 설령 제대로 매치되더라도 - 동작하지 않는다. 이 때는 패턴에 /m
플래그를 붙여주어야 한다)
my $str = ' Buster and Mimi';
print join(":", split //, $str);
print join(":", split /z*/, $str);
print join(":", split / /, $str);
print join(":", split ' ', $str);
$str = "Line 1\nLine 2\nLine 3";
print join(":", split /^/, $str);
2. 하나의 스트링을 인코딩에 관계없이 일정 갯수의 문자로 나누기
2007년에 끙끙댔던 문제인데, 막상 답을 알고 나니 매우 간단해져서, 과거 내용은 삭제함
- Perl 5.8 이후로는, Encode 모듈이 있으므로, 문자열을 일단 decode하고 나면 한글이든 영문이든 어떤 캐릭터든 "글자 한 개"로 간주되니까 substr 을 쓰든 정규표현식을 쓰든 그냥 쪼개면 된다.
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와 한] [글]
- Perl 5.6과 그 이전 버전에서는 (굳이 그 오래된 버전을 써야 한다면) Text::Iconv 모듈을 사용해서 UTF-16 등 한 문자가 2바이트로 고정된 인코딩으로 바꾸고, 2바이트 단위로 처리한 다음에 다시 원래 인코딩으로 바꿔주면 되겠다.
기타
컴퓨터분류