Perl Unicode 쿡북
Perl Unicode 쿡북 (perlunicook)
Perl에서 흔한 Unicode 작업을 처리하는 짧은 요리법(레시피) 모음이에요. 개별 레시피에서 선언되지 않은 변수는 이전에 적절한 값이 들어 있다고 가정해요. 마지막에는 완전한 프로그램 하나도 있어요.
예제들 (EXAMPLES)
℞ 0: 표준 서문 (Standard preamble)
달리 명시하지 않는 한, 아래 모든 예제는 이 표준 서문이 있어야 제대로 동작해요. #!은 시스템에 맞게 조정하세요:
#!/usr/bin/env perl
use v5.36; # or later to get "unicode_strings" feature,
# plus strict, warnings
use utf8; # so literals and identifiers can be in UTF-8
use warnings qw(FATAL utf8); # fatalize encoding glitches
use open qw(:std :encoding(UTF-8)); # undeclared streams in UTF-8
use charnames qw(:full :short); # unneeded in v5.16
이 서문은 유닉스 프로그래머에게도 이진 스트림을 binmode 하거나 :raw로 열도록 만드는데, 그래야만 이식 가능하게 접근할 수 있기 때문이에요.
경고: use autodie(2.26 이전)와 use open은 서로 잘 지내지 못해요.
℞ 1: 일반 Unicode 인식 필터 (Generic Unicode-savvy filter)
들어올 때 항상 decompose하고, 나갈 때 recompose하세요.
use Unicode::Normalize;
while (<>) {
$_ = NFD($_); # decompose + reorder canonically
...
} continue {
print NFC($_); # recompose (where possible) + reorder canonically
}
℞ 2: Unicode 경고 미세 조정 (Fine-tuning Unicode warnings)
v5.14부터 Perl은 UTF-8 경고의 세 가지 하위 클래스를 구분해요.
use v5.14; # subwarnings unavailable any earlier
no warnings "nonchar"; # the 66 forbidden non-characters
no warnings "surrogate"; # UTF-16/CESU-8 nonsense
no warnings "non_unicode"; # for codepoints over 0x10_FFFF
℞ 3: 식별자와 리터럴을 위해 소스를 utf8로 선언 (Declare source in utf8)
매우 중요한 use utf8 선언이 없으면, 리터럴과 식별자에 UTF-8을 넣는 게 제대로 동작하지 않아요. 위 표준 서문을 썼다면 이미 됐어요. 그러면 이런 것들을 할 수 있어요:
use utf8;
my $measure = "Ångström";
my @μsoft = qw( cp852 cp1251 cp1252 );
my @ὑπέρμεγας = qw( ὑπέρ μεγας );
my @鯉 = qw( koi8-f koi8-u koi8-r );
my $motto = "👪 💗 🐪"; # FAMILY, GROWING HEART, DROMEDARY CAMEL
use utf8를 잊으면 높은 바이트가 별개의 문자로 오해되어 아무것도 제대로 동작하지 않아요.
℞ 4: 문자와 그 숫자 (Characters and their numbers)
ord와 chr 함수는 ASCII만이 아니라 모든 코드 포인트에서 투명하게 동작해요. 실제로는 Unicode에만 국한되지도 않아요.
# ASCII characters
ord("A")
chr(65)
# characters from the Basic Multilingual Plane
ord("Σ")
chr(0x3A3)
# beyond the BMP
ord("𝑛") # MATHEMATICAL ITALIC SMALL N
chr(0x1D45B)
# beyond Unicode! (up to MAXINT)
ord("\x{20_0000}")
chr(0x20_0000)
℞ 5: 문자 번호로 Unicode 리터럴 (Unicode literals by character number)
보간되는 리터럴(큰따옴표 문자열이든 정규식이든)에서 \x{HHHHHH} 이스케이프로 문자 번호를 지정해 문자를 표현할 수 있어요.
String: "\x{3a3}"
Regex: /\x{3a3}/
String: "\x{1d45b}"
Regex: /\x{1d45b}/
# even non-BMP ranges in regex work fine
/[\x{1D434}-\x{1D467}]/
℞ 6: 문자 번호로 이름 얻기 (Get character name by number)
use charnames ();
my $name = charnames::viacode(0x03A3);
℞ 7: 이름으로 문자 번호 얻기 (Get character number by name)
use charnames ();
my $number = charnames::vianame("GREEK CAPITAL LETTER SIGMA");
℞ 8: Unicode 명명 문자 (Unicode named characters)
\N{charname} 표기를 쓰면 그 이름의 문자를 보간 리터럴(큰따옴표 문자열과 정규식)에서 얻을 수 있어요. v5.16에는 암묵적인
use charnames qw(:full :short);
이 있지만, v5.16 이전에는 어떤 charnames 집합을 원하는지 명시해야 해요. :full 이름은 공식 Unicode 문자 이름·별칭·시퀀스로, 모두 하나의 네임스페이스를 공유해요.
use charnames qw(:full :short latin greek);
"\N{MATHEMATICAL ITALIC SMALL N}" # :full
"\N{GREEK CAPITAL LETTER SIGMA}" # :full
그 외는 Perl 특유의 편의 축약이에요. 스크립트별 짧은 이름을 원한다면, 이름으로 스크립트를 하나 이상 지정하세요.
"\N{Greek:Sigma}" # :short
"\N{ae}" # latin
"\N{epsilon}" # greek
v5.16 릴리스는 문자 이름의 느슨한 매칭을 위한 :loose import도 지원하는데, 속성 이름의 느슨한 매칭과 똑같이 동작해요 — 대소문자·공백·밑줄을 무시하죠:
"\N{euro sign}" # :loose (from v5.16)
v5.32부터는 정규식에서 공식 Unicode 명명 문자를 얻는
qr/\p{name=euro sign}/
도 쓸 수 있어요. 이때는 항상 느슨한 매칭이 적용돼요.
℞ 9: Unicode 명명 시퀀스 (Unicode named sequences)
이것들은 문자 이름처럼 보이지만 여러 코드 포인트를 반환해요. printf의 %vx 벡터 출력 기능을 주목하세요.
use charnames qw(:full);
my $seq = "\N{LATIN CAPITAL LETTER A WITH MACRON AND GRAVE}";
printf "U+%v04X\n", $seq;
U+0100.0300
℞ 10: 사용자 정의 명명 문자 (Custom named characters)
:alias로 기존 문자에 자기만의 어휘 스코프 별명을 주거나, 이름 없는 개인 사용(private-use) 문자에 유용한 이름을 줄 수 있어요.
use charnames ":full", ":alias" => {
ecute => "LATIN SMALL LETTER E WITH ACUTE",
"APPLE LOGO" => 0xF8FF, # private use character
};
"\N{ecute}"
"\N{APPLE LOGO}"
℞ 11: CJK 코드 포인트의 이름 (Names of CJK codepoints)
"東京" 같은 한자(Sinogram)는 "이름"이 다양해서 CJK UNIFIED IDEOGRAPH-6771, CJK UNIFIED IDEOGRAPH-4EAC 같은 문자 이름으로 돌아와요. CPAN의 Unicode::Unihan 모듈은 그 출력을 이해하는 법만 알면 이를(그리고 훨씬 더) 디코딩하는 큰 데이터베이스를 갖고 있어요.
# cpan -i Unicode::Unihan
use Unicode::Unihan;
my $str = "東京";
my $unhan = Unicode::Unihan->new;
for my $lang (qw(Mandarin Cantonese Korean JapaneseOn JapaneseKun)) {
printf "CJK $str in %-12s is ", $lang;
say $unhan->$lang($str);
}
출력:
CJK 東京 in Mandarin is DONG1JING1
CJK 東京 in Cantonese is dung1ging1
CJK 東京 in Korean is TONGKYENG
CJK 東京 in JapaneseOn is TOUKYOU KEI KIN
CJK 東京 in JapaneseKun is HIGASHI AZUMAMIYAKO
특정 로마자 표기 체계를 염두에 두고 있다면 특정 모듈을 쓰세요:
# cpan -i Lingua::JA::Romanize::Japanese
use Lingua::JA::Romanize::Japanese;
my $k2r = Lingua::JA::Romanize::Japanese->new;
my $str = "東京";
say "Japanese for $str is ", $k2r->chars($str);
출력:
Japanese for 東京 is toukyou
℞ 12: 명시적 encode/decode (Explicit encode/decode)
드물게, 데이터베이스 읽기 같은 경우 디코드해야 하는 인코딩된 텍스트를 받을 수 있어요.
use Encode qw(encode decode);
my $chars = decode("shiftjis", $bytes, 1);
# OR
my $bytes = encode("MIME-Header-ISO_2022_JP", $chars, 1);
모두 같은 인코딩인 스트림에는 encode/decode를 쓰지 말고, 파일을 열 때 파일 인코딩을 설정하거나 아래 설명대로 직후에 binmode로 설정하세요.
℞ 13: 프로그램 인자를 utf8로 디코드 (Decode program arguments as utf8)
$ perl -CA ...
or
$ export PERL_UNICODE=A
or
use Encode qw(decode);
@ARGV = map { decode('UTF-8', $_, 1) } @ARGV;
℞ 14: 프로그램 인자를 locale 인코딩으로 디코드 (Decode program arguments as locale encoding)
# cpan -i Encode::Locale
use Encode qw(locale);
use Encode::Locale;
# use "locale" as an arg to encode/decode
@ARGV = map { decode(locale => $_, 1) } @ARGV;
℞ 15: STD{IN,OUT,ERR}를 utf8로 선언 (Declare STD{IN,OUT,ERR} to be utf8)
명령행 옵션, 환경 변수, 또는 binmode를 명시적으로 호출해요:
$ perl -CS ...
or
$ export PERL_UNICODE=S
or
use open qw(:std :encoding(UTF-8));
or
binmode(STDIN, ":encoding(UTF-8)");
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");
℞ 16: STD{IN,OUT,ERR}를 locale 인코딩으로 선언 (Declare STD{IN,OUT,ERR} to be in locale encoding)
# cpan -i Encode::Locale
use Encode;
use Encode::Locale;
# or as a stream for binmode or open
binmode STDIN, ":encoding(console_in)" if -t STDIN;
binmode STDOUT, ":encoding(console_out)" if -t STDOUT;
binmode STDERR, ":encoding(console_out)" if -t STDERR;
℞ 17: 파일 I/O를 기본 utf8로 (Make file I/O default to utf8)
인코딩 인자 없이 연 파일은 UTF-8이 돼요:
$ perl -CD ...
or
$ export PERL_UNICODE=D
or
use open qw(:encoding(UTF-8));
℞ 18: 모든 I/O와 인자를 기본 utf8로 (Make all I/O and args default to utf8)
$ perl -CSDA ...
or
$ export PERL_UNICODE=SDA
or
use open qw(:std :encoding(UTF-8));
use Encode qw(decode);
@ARGV = map { decode('UTF-8', $_, 1) } @ARGV;
℞ 19: 특정 인코딩으로 파일 열기 (Open file with specific encoding)
스트림 인코딩을 지정해요. 인코딩된 텍스트를 다루는 정상적인 방법은 저수준 함수를 부르는 게 아니라 이렇게 하는 거예요.
# input file
open(my $in_file, "< :encoding(UTF-16)", "wintext");
OR
open(my $in_file, "<", "wintext");
binmode($in_file, ":encoding(UTF-16)");
THEN
my $line = <$in_file>;
# output file
open($out_file, "> :encoding(cp1252)", "wintext");
OR
open(my $out_file, ">", "wintext");
binmode($out_file, ":encoding(cp1252)");
THEN
print $out_file "some text\n";
인코딩보다 더 많은 레이어를 지정할 수 있어요. 예를 들어 :raw :encoding(UTF-16LE) :crlf라는 주문은 암묵적인 CRLF 처리를 포함해요.
℞ 20: Unicode 대소문자 (Unicode casing)
Unicode 대소문자는 ASCII 대소문자와 매우 달라요.
uc("henry ⅷ") # "HENRY Ⅷ"
uc("tschüß") # "TSCHÜSS" notice ß => SS
# both are true:
"tschüß" =~ /TSCHÜSS/i # notice ß => SS
"Σίσυφος" =~ /ΣΊΣΥΦΟΣ/i # notice Σ,σ,ς sameness
℞ 21: Unicode 대소문자 무시 비교 (Unicode case-insensitive comparisons)
CPAN의 Unicode::CaseFold 모듈에서도 쓸 수 있고, v5.16의 새 fc "foldcase" 함수는 /i 패턴 수정자가 항상 써 온 것과 같은 Unicode casefolding에 접근하게 해줘요:
use feature "fc"; # fc() function is from v5.16
# sort case-insensitively
my @sorted = sort { fc($a) cmp fc($b) } @list;
# both are true:
fc("tschüß") eq fc("TSCHÜSS")
fc("Σίσυφος") eq fc("ΣΊΣΥΦΟΣ")
℞ 22: 정규식에서 Unicode 줄바꿈 시퀀스 매치 (Match Unicode linebreak sequence in regex)
Unicode 줄바꿈은 두 문자 CRLF grapheme 또는 일곱 개의 세로 공백 문자 중 하나와 매치돼요. 서로 다른 운영 체제에서 온 텍스트 파일을 다루는 데 좋아요.
\R
s/\R/\n/g; # normalize all linebreaks to \n
℞ 23: 문자 카테고리 얻기 (Get character category)
숫자 코드 포인트의 일반 카테고리를 찾아요.
use Unicode::UCD qw(charinfo);
my $cat = charinfo(0x3A3)->{category}; # "Lu"
℞ 24: 내장 문자 클래스의 Unicode 인식 끄기 (Disabling Unicode-awareness in builtin charclasses)
\w, \b, \s, \d, POSIX 클래스가 이 스코프에서 또는 정규식 하나에서만 Unicode에서 제대로 동작하지 않게 해요.
use v5.14;
use re "/a";
# OR
my($num) = $str =~ /(\d+)/a;
또는 \p{ahex}, \p{POSIX_Digit} 같은 특정한 비-Unicode 속성을 쓰세요. 속성은 어떤 charset 수정자(/d /u /l /a /aa)가 적용돼 있든 정상적으로 동작해요.
℞ 25: 정규식에서 \p, \P로 Unicode 속성 매치 (Match Unicode properties in regex)
이들은 모두 주어진 속성을 가진 단일 코드 포인트와 매치돼요. 그 속성이 없는 코드 포인트 하나를 매치하려면 \p 대신 \P를 쓰세요.
\pL, \pN, \pS, \pP, \pM, \pZ, \pC
\p{Sk}, \p{Ps}, \p{Lt}
\p{alpha}, \p{upper}, \p{lower}
\p{Latin}, \p{Greek}
\p{script_extensions=Latin}, \p{scx=Greek}
\p{East_Asian_Width=Wide}, \p{EA=W}
\p{Line_Break=Hyphen}, \p{LB=HY}
\p{Numeric_Value=4}, \p{NV=4}
℞ 26: 사용자 정의 문자 속성 (Custom character properties)
컴파일 시점에 정규식에 쓸 자기만의 사용자 정의 문자 속성을 정의해요.
# using private-use characters
sub In_Tengwar { "E000\tE07F\n" }
if (/\p{In_Tengwar}/) { ... }
# blending existing properties
sub Is_GraecoRoman_Title {<<'END_OF_SET'}
+utf8::IsLatin
+utf8::IsGreek
&utf8::IsTitle
END_OF_SET
if (/\p{Is_GraecoRoman_Title}/) { ... }
℞ 27: Unicode 정규화 (Unicode normalization)
전형적으로 입력 시 NFD로, 출력 시 NFC로 렌더링해요. NFKC나 NFKD 함수를 쓰면 검색 대상 텍스트에도 이미 같은 처리를 했다면 검색 적중률이 좋아져요. 이는 단순히 미리 결합된 호환성 글리프 이상을 다룬다는 것에 주의하세요. 표준 결합 클래스에 따라 마크를 재정렬하고 단일항(singleton)을 걸러내기도 해요.
use Unicode::Normalize;
my $nfd = NFD($orig);
my $nfc = NFC($orig);
my $nfkd = NFKD($orig);
my $nfkc = NFKC($orig);
℞ 28: non-ASCII Unicode 숫자 변환 (Convert non-ASCII Unicode numerics)
/a나 /aa를 쓰지 않았다면 \d는 ASCII 숫자보다 더 많이 매치하지만, Perl의 암묵적 문자열-숫자 변환은 현재 이것들을 인식하지 못해요. 그런 문자열을 수동으로 변환하는 방법이에요.
use utf8;
use v5.14; # needed for num() function
use Unicode::UCD qw(num);
my $str = "got Ⅻ and ४५६७ and ⅞ and 兆 here";
my @nums = ();
while ($str =~ /(\d+|\N)/g) { # not just ASCII!
push @nums, num($1);
}
say "@nums"; # 12 4567 0.875 1000000000000
use charnames qw(:full);
my $nv = num("\N{VAI DIGIT ONE}\N{VAI DIGIT TWO}");
say $nv; # 12
℞ 29: 정규식에서 Unicode grapheme cluster 매치 (Match Unicode grapheme cluster in regex)
프로그래머가 보는 "문자"는 /./s가 매치하는 코드 포인트이지만, 사용자가 보는 "문자"는 /\X/가 매치하는 grapheme이에요.
# Find vowel *plus* any combining diacritics, underlining, etc.
my $nfd = NFD($orig);
$nfd =~ / (?=[aeiou]) \X /xi
℞ 30: 코드 포인트 대신 grapheme으로 추출 (정규식) (Extract by grapheme instead of by codepoint (regex))
# match and grab five first graphemes
my($first_five) = $str =~ /^ ( \X{5} ) /x;
℞ 31: 코드 포인트 대신 grapheme으로 추출 (substr) (Extract by grapheme instead of by codepoint (substr))
# cpan -i Unicode::GCString
use Unicode::GCString;
my $gcs = Unicode::GCString->new($str);
my $first_five = $gcs->substr(0, 5);
℞ 32: grapheme으로 문자열 뒤집기 (Reverse string by grapheme)
코드 포인트로 뒤집으면 분음 기호가 망가져서, crème brûlée가 eél̂urb em̀erc가 아니라 éel̂urb em̀erc로 잘못 변환돼요. 그러니 grapheme으로 뒤집으세요. 두 방법 모두 문자열이 어떤 정규화 상태든 제대로 동작해요:
$str = join("", reverse $str =~ /\X/g);
# OR: cpan -i Unicode::GCString
use Unicode::GCString;
$str = reverse Unicode::GCString->new($str);
℞ 33: grapheme의 문자열 길이 (String length in graphemes)
brûlée 문자열은 grapheme이 여섯 개지만 코드 포인트는 많게는 여덟 개예요. 이는 코드 포인트가 아니라 grapheme으로 셉니다:
my $str = "brûlée";
my $count = 0;
while ($str =~ /\X/g) { $count++ }
# OR: cpan -i Unicode::GCString
use Unicode::GCString;
my $gcs = Unicode::GCString->new($str);
my $count = $gcs->length;
℞ 34: 출력용 Unicode 열 너비 (Unicode column-width for printing)
Perl의 printf, sprintf, format은 모든 코드 포인트가 인쇄 열 1칸을 차지한다고 생각하지만, 많은 문자는 0칸이나 2칸을 차지해요. 정규화가 차이를 만들지 않는다는 걸 보여주려고 두 형태를 모두 출력해요:
use Unicode::GCString;
use Unicode::Normalize;
my @words = qw/crème brûlée/;
@words = map { NFC($_), NFD($_) } @words;
for my $str (@words) {
my $gcs = Unicode::GCString->new($str);
my $cols = $gcs->columns;
my $pad = " " x (10 - $cols);
say $str, $pad, " |";
}
이것은 정규화와 무관하게 제대로 패딩함을 보여주는 출력을 만들어요:
crème |
crème |
brûlée |
brûlée |
℞ 35: Unicode 정렬 (Unicode collation)
숫자 코드 포인트로 정렬된 텍스트는 합리적인 알파벳 순서를 따르지 않아요. 텍스트 정렬에는 UCA를 쓰세요.
use Unicode::Collate;
my $col = Unicode::Collate->new();
my @list = $col->sort(@old_list);
이 모듈에 편리한 명령행 인터페이스는 Unicode::Tussle CPAN 모듈의 ucsort 프로그램을 참고하세요.
℞ 36: 대소문자·억양 무시 Unicode 정렬 (Case- and accent-insensitive Unicode sort)
콜레이션 강도(level)를 1로 지정하면 대소문자와 분음 기호를 무시하고 기본 문자만 보게 돼요.
use Unicode::Collate;
my $col = Unicode::Collate->new(level => 1);
my @list = $col->sort(@old_list);
℞ 37: Unicode locale 정렬 (Unicode locale collation)
일부 locale은 특별한 정렬 규칙이 있어요.
# either use v5.12, OR: cpan -i Unicode::Collate::Locale
use Unicode::Collate::Locale;
my $col = Unicode::Collate::Locale->new(locale => "de__phonebook");
my @list = $col->sort(@old_list);
위에서 언급한 ucsort 프로그램은 --locale 매개변수를 받아들여요.
℞ 38: cmp가 코드 포인트 대신 텍스트로 동작하게 (Making cmp work on text instead of codepoints)
이렇게 하는 대신:
@srecs = sort {
$b->{AGE} <=> $a->{AGE}
||
$a->{NAME} cmp $b->{NAME}
} @recs;
이렇게 해요:
my $coll = Unicode::Collate->new();
for my $rec (@recs) {
$rec->{NAME_key} = $coll->getSortKey( $rec->{NAME} );
}
@srecs = sort {
$b->{AGE} <=> $a->{AGE}
||
$a->{NAME_key} cmp $b->{NAME_key}
} @recs;
℞ 39: 대소문자·억양 무시 비교 (Case- and accent-insensitive comparisons)
콜레이터 객체를 써서 Unicode 텍스트를 코드 포인트가 아니라 문자로 비교해요.
use Unicode::Collate;
my $es = Unicode::Collate->new(
level => 1,
normalization => undef
);
# now both are true:
$es->eq("García", "GARCIA" );
$es->eq("Márquez", "MARQUEZ");
℞ 40: 대소문자·억양 무시 locale 비교 (Case- and accent-insensitive locale comparisons)
같지만 특정 locale에서요.
my $de = Unicode::Collate::Locale->new(
locale => "de__phonebook",
);
# now this is true:
$de->eq("tschüß", "TSCHUESS"); # notice ü => UE, ß => SS
℞ 41: Unicode 줄바꿈 (Unicode linebreaking)
Unicode 규칙에 따라 텍스트를 줄로 나눠요.
# cpan -i Unicode::LineBreak
use Unicode::LineBreak;
use charnames qw(:full);
my $para = "This is a super\N{HYPHEN}long string. " x 20;
my $fmt = Unicode::LineBreak->new;
print $fmt->break($para), "\n";
℞ 42: DBM 해시의 Unicode 텍스트, 번거로운 방법 (Unicode text in DBM hashes, the tedious way)
DBM 해시의 키·값으로 일반 Perl 문자열을 쓰면, 바이트에 안 들어맞는 코드 포인트가 있으면 wide character 예외가 발생해요. 변환을 수동으로 관리하는 방법:
use DB_File;
use Encode qw(encode decode);
tie %dbhash, "DB_File", "pathname";
# STORE
# assume $uni_key and $uni_value are abstract Unicode strings
my $enc_key = encode("UTF-8", $uni_key, 1);
my $enc_value = encode("UTF-8", $uni_value, 1);
$dbhash{$enc_key} = $enc_value;
# FETCH
# assume $uni_key holds a normal Perl string (abstract Unicode)
my $enc_key = encode("UTF-8", $uni_key, 1);
my $enc_value = $dbhash{$enc_key};
my $uni_value = decode("UTF-8", $enc_value, 1);
℞ 43: DBM 해시의 Unicode 텍스트, 쉬운 방법 (Unicode text in DBM hashes, the easy way)
변환을 암묵적으로 관리하는 방법이에요. 특정 인코딩이 붙은 스트림처럼 모든 인코딩·디코딩이 자동으로 이뤄져요:
use DB_File;
use DBM_Filter;
my $dbobj = tie %dbhash, "DB_File", "pathname";
$dbobj->Filter_Value("utf8"); # this is the magic bit
# STORE
# assume $uni_key and $uni_value are abstract Unicode strings
$dbhash{$uni_key} = $uni_value;
# FETCH
# $uni_key holds a normal Perl string (abstract Unicode)
my $uni_value = $dbhash{$uni_key};
℞ 44: 프로그램: Unicode 정렬·인쇄 데모 (PROGRAM: Demo of Unicode collation and printing)
locale에 민감한 정렬, Unicode 대소문자, 일부 문자가 매번 한 칸이 아니라 0칸이나 2칸을 차지할 때의 인쇄 너비 관리를 어떻게 쓰는지 보여주는 전체 프로그램이에요. 실행하면 이렇게 예쁘게 정렬된 출력을 만들어요:
Crème Brûlée....... €2.00
Éclair............. €1.60
Fideuà............. €4.20
Hamburger.......... €6.00
Jamón Serrano...... €4.45
Linguiça........... €7.00
Pâté............... €4.15
Pears.............. €2.00
Pêches............. €2.25
Smørbrød........... €5.75
Spätzle............ €5.50
Xoriço............. €3.00
Γύρος.............. €6.50
막걸리............. €4.00
おもち............. €2.65
お好み焼き......... €8.00
シュークリーム..... €1.85
寿司............... €9.99
包子............... €7.50
그 프로그램:
#!/usr/bin/env perl
# umenu - demo sorting and printing of Unicode food
#
# (obligatory and increasingly long preamble)
#
use v5.36;
use utf8;
use warnings qw(FATAL utf8); # fatalize encoding faults
use open qw(:std :encoding(UTF-8)); # undeclared streams in UTF-8
use charnames qw(:full :short); # unneeded in v5.16
# std modules
use Unicode::Normalize; # std perl distro as of v5.8
use List::Util qw(max); # std perl distro as of v5.10
use Unicode::Collate::Locale; # std perl distro as of v5.14
# cpan modules
use Unicode::GCString; # from CPAN
my %price = (
"γύρος" => 6.50, # gyros
"pears" => 2.00, # like um, pears
"linguiça" => 7.00, # spicy sausage, Portuguese
"xoriço" => 3.00, # chorizo sausage, Catalan
"hamburger" => 6.00, # burgermeister meisterburger
"éclair" => 1.60, # dessert, French
"smørbrød" => 5.75, # sandwiches, Norwegian
"spätzle" => 5.50, # Bayerisch noodles, little sparrows
"包子" => 7.50, # bao1 zi5, steamed pork buns, Mandarin
"jamón serrano" => 4.45, # country ham, Spanish
"pêches" => 2.25, # peaches, French
"シュークリーム" => 1.85, # cream-filled pastry like eclair
"막걸리" => 4.00, # makgeolli, Korean rice wine
"寿司" => 9.99, # sushi, Japanese
"おもち" => 2.65, # omochi, rice cakes, Japanese
"crème brûlée" => 2.00, # crema catalana
"fideuà" => 4.20, # more noodles, Valencian
# (Catalan=fideuada)
"pâté" => 4.15, # goose liver paste, French
"お好み焼き" => 8.00, # okonomiyaki, Japanese
);
my $width = 5 + max map { colwidth($_) } keys %price;
# So the Asian stuff comes out in an order that someone
# who reads those scripts won't freak out over; the
# CJK stuff will be in JIS X 0208 order that way.
my $coll = Unicode::Collate::Locale->new(locale => "ja");
for my $item ($coll->sort(keys %price)) {
print pad(entitle($item), $width, ".");
printf " €%.2f\n", $price{$item};
}
sub pad ($str, $width, $padchar) {
return $str . ($padchar x ($width - colwidth($str)));
}
sub colwidth ($str) {
return Unicode::GCString->new($str)->columns;
}
sub entitle ($str) {
$str =~ s{ (?=\pL)(\S) (\S*) }
{ ucfirst($1) . lc($2) }xge;
return $str;
}
함께 보기 (SEE ALSO)
참고할 manpage는 (일부는 CPAN 모듈): perlunicode, perluniprops, perlre, perlrecharclass, perluniintro, perlunitut, perlunifaq, PerlIO, DB_File, DBM_Filter, DBM_Filter::utf8, Encode, Encode::Locale, Unicode::UCD, Unicode::Normalize, Unicode::GCString, Unicode::LineBreak, Unicode::Collate, Unicode::Collate::Locale, Unicode::Unihan, Unicode::CaseFold, Unicode::Tussle, Lingua::JA::Romanize::Japanese, Lingua::ZH::Romanize::Pinyin, Lingua::KO::Romanize::Hangul.
Unicode::Tussle CPAN 모듈은 Unicode 작업을 돕는 많은 프로그램을 포함하는데, 표준 유틸리티를 완전히·부분적으로 대체하는 것들이 있어요: egrep 대신 tcgrep, cat -v·hexdump 대신 uniquote, wc 대신 uniwc, look 대신 unilook, fmt 대신 unifmt, sort 대신 ucsort. Unicode 문자 이름·속성 탐색에는 uniprops, unichars, uninames 프로그램을 보세요. 또한 모두 Unicode-y한 일을 하는 일반 필터인 다음 프로그램들도 제공해요: unititle·unicaps; uniwide·uninarrow; unisupers·unisubs; nfd·nfc·nfkd·nfkc; 그리고 uc·lc·tc.
마지막으로 발행된 Unicode 표준(쪽수는 버전 6.0.0 기준)과 그 특정 부록·기술 보고서를 보세요:
- §3.13 Default Case Algorithms, 113쪽; §4.2 Case, 120–122쪽; Case Mappings, 166–172쪽, 특히 170쪽부터 시작하는 Caseless Matching.
- UAX #44: Unicode Character Database
- UTS #18: Unicode Regular Expressions
- UAX #15: Unicode Normalization Forms
- UTS #10: Unicode Collation Algorithm
- UAX #29: Unicode Text Segmentation
- UAX #14: Unicode Line Breaking Algorithm
- UAX #11: East Asian Width
저자·저작권·라이선스 (AUTHOR, COPYRIGHT AND LICENCE)
Tom Christiansen <[email protected]>이 작성했고, 뒤에서 Larry Wall과 Jeffrey Friedl이 가끔 참견했어요. Copyright © 2012 Tom Christiansen. 이 프로그램은 자유 소프트웨어이며, Perl 자체와 같은 조건으로 재배포·수정할 수 있어요.
대부분의 예제는 현재 판의 "Camel Book", 즉 O'Reilly Media의 2012-02-13 발행 Programming Perl 4판(Copyright © 2012 Tom Christiansen 등)에서 가져왔어요. 코드 자체는 자유롭게 재배포 가능하고, 이 manpage의 어떤 예제든 아무 조건 없이 자기 프로그램에 넣으려고 이식·접기·꼬기·훼손해도 좋아요. 코드 주석으로 출처를 밝히는 건 예의지만 필수는 아니에요.
더 알아보기 (Learn more)
perlunicode,perluniintro,perlunifaq,perlunitut— Perl Unicode 문서군Unicode::UCD,Unicode::Normalize,Encode— Unicode 작업 핵심 모듈