perl は 5.8 から Unicode(utf-8) がサポートされました.5.6 でも Unicode に対応はしていましたが,ぜんぜん使い物にならず,ようやく 5.8 でまともに使えるようになったということです.ただせっかく使えるにもかか わらず perldoc などを見てもイマイチ使い方がわからないので,独自にまと めてみたのがこのページです.
誤った書き方や勘違いをしてい ることもあるので,形式的ですがこのページの内容は無保証です.
とりあえず perl5.8 で新しく組み込まれた機能を見るために,euc-jp から shift_jis への変換スクリプトをいくつか載せます.
open my $in, "<:encoding(euc-jp)", $infile or die; open my $out, ">:encoding(shift_jis)", $outfile or die; while(<$in>){ print $out $_; }
use Encode qw(from_to); open my $in, "<", $infile or die; open my $out, ">", $outfile or die; while(<$in>){ from_to($_, "euc-jp", "shift_jis"); print $out $_; }
use encoding 'euc-jp', STDOUT=>'shift_jis' while(<>){ print $_; } # 別の書き方 use encoding 'shift_jis', STDIN=>'euc-jp'; while(<>){ print $_; }
binmode(STDIN, ":encoding(euc-jp)"); binmode(STDOUT,":encoding(shift_jis)"); while (<>) { print $_; }(注)パイプやリダイレクトではなく perl hoge.pl data.txt のように 引数にファイルを指定するやり方では,binmode で入力文字コードを指定する ことができません.binmode は既に open されているファイルハンドルに対 して文字コードを指定しますが(STD〜は最初から open),引数で指 定されたファイルは初めて <> が出現した時点 open される(ファイル ハンドルは ARGV)ので指定出来ません.強引に binmode で指定するとしたら, 次のようになるでしょう.
$first_row = <>; # 1行目は捨てる(あり得ない・・・) binmode(ARGV, ":encoding(euc-jp)"); binmode(STDOUT,":encoding(shift_jis)"); while (<>) { print $_; }
perl5.8 では,ファイルの入出力にシステムの stdio に代わり PerlIO をデフォルトで使用するようになりました.perlIO は レイヤー という概念によりファイルハンドルの振る舞いを 制御することができます.レイヤーを指定するには,open 関数に3つの引数を 指定します.
open $in, "<:encoding(euc-jp)", $infile; open $out,">:crlf :utf8", $outfile;
2番目の引数がレイヤーの指定で,旧来の stdio の使用を指定したりでき ますが,ここでは文字コードの指定もできます.扱うファイルの文字コードが utf8 であってもレイヤーに :utf8 と指定することで, perl にテキストファイルであることを示します.そのほかの文字コードの場 合は,:encoding() を指定します.
ファイルの open 時に文字コードを指定できますが,すでに開いたファイ ルのファイルハンドルの文字コードも指定できます. STDIN や STDOUT も binmode で指定できます.
binmode($in,":encoding(shift_jis)"); binmode(STDIN,":encoding(euc-jp)"); binmode(STDOUT,":utf8");
open プラグマで指定しておくこともできます.この場合,open の引数では 文字コードを指定することはできません.
use open ":encoding(euc-jp)"; open $in, "<", $filename;
#!/usr/bin/perl use encoding 'shift-jis'; while ( <> ) { tr/ぁ-ん/ァ-ン/; print; } # ./sjis_script.pl < sjis.fileuse encoding でスクリプトの文字コードを指定します.こ うすることで自動的に標準入出力(STDIN,STDOUT)の文字コードも指定した文字 コードで処理することができます. use encoding で指定した以外の文字コードの標準入出力を扱う場合は use encoding の引数で指定します.
use encoding 'shift-jis', STDIN=>'utf8'; # 標準入力:utf8,標準出力:shift-jis use encoding 'shift-jis', STDOUT=>'utf8'; # 標準入力:shift-jis,標準出力:utf8 use encoding 'shift-jis', STDIN=>'utf8', STDOUT=>'utf8' # 標準入出力:utf8;上記のように use encoding で文字コードを指定した場合 binmode では標準入出 力の文字コードは指定できないようです(ただし,Filter=>1 を使用すれば binmode で指定できる). use encoding では標準入出力の文字コードだけが指定できるので,標準入出力以 外のファイルを開く場合は use open でデフォルトの文字コードを指定します.
#!/usr/bin/perl use encoding 'shift-jis'; use open ':encoding(shift-jis)'; open my $in,"< $ARGV[0]" or die; open my $out,"> $ARGV[1]" or die; while (<$in>) { tr/ぁ-ん/ァ-ン/; print $out $_; } # ./sjis_script.pl sjis.file1 sjis.file2use open も入力と出力のファイルに対して別々に文字コードを指定すること ができます.
use open ':encoding(shift-jis)'; # 入出力:shift-jis use open OUT=>':encoding(euc-jp)'; # 入力:デフォルト,出力:euc-jp use open IN=>'utf8', OUT=>':encoding(euc-jp)' # 入力:utf8,出力:euc-jpまた,関数 open の第2引数として文字コードを指定することもできます.
#!/usr/bin/perl use encoding 'shift-jis'; open my $in,"<:encoding(shift-jis)", $ARGV[0] or die; open my $out,">:encoding(shift-jis)", $ARGV[1] or die; while (<$in>) { tr/ぁ-ん/ァ-ン/; print $out $_; } # ./sjis_script.pl sjis.file1 sjis.file2binmode を使用して後から文字コードを指定することもできます.
#!/usr/bin/perl use encoding 'shift-jis'; open my $in,"< $ARGV[0]" or die; open my $out,"> $ARGV[1]" or die; binmode($in,":encoding(shift-jis)"); binmode($out,":encoding(shift-jis)"); while (<$in>) { tr/ぁ-ん/ァ-ン/; print $out $_; } # ./sjis_script.pl sjis.file1 sjis.file2なお標準エラー出力の文字コードを指定するには binmode だけが使えるようです.
binmode(STDERR,":encoding(shift-jis)");
#!/usr/bin/perl use encoding 'euc-jp', STDIN=>'utf8', STDOUT=>'utf8'; use open 'utf8'; while ( <> ) { tr/ぁ-ん/ァ-ン/; print; } # ./eucjp_scirpt.pl < utf8.file # ./eucjp_script.pl utf8.fileスクリプトを utf8 で記述した場合で同様なことをするためには, 下記のように少し冗長になってしまいます (もっと簡潔な書き方があったら教えてください).
#!/usr/bin/perl use utf8; use open 'encoding(euc-jp)'; binmode(STDIN,":encoding(euc-jp)"); binmode(STDOUT,":encoding(euc-jp)"); while (<>) { tr/ぁ-ん/ァ-ン/; print; } # ./utf8_scirpt.pl < eucjp.file # ./utf8_script.pl eucjp.file
perl5.8 では,Unicode 文字列に対して内部で UTF-8 フラグを付与しています. このフラグより文字列がただのバイト列であるか Unicode 文字列であるかを 区別しています.ファイルを読み込む際にエンコードを指定すれば, 読み込んだ文字列は UTF-8 に変換され,自動的に UTF-8 フラグが付与されます (つまり何も指定しなければ,perl5.8 は Unicode 文字列として扱いません). UTF-8 フラグが付与されているかは,Encode::is_utf8() を用います (perl5.8.1 からは utf8::is_utf8() も使用できます).
# cat > is_utf8.pl use Encode; $string = <>; print "is_utf8: ", Encode::is_utf8($string) ? 1 : 0, "\n"; binmode STDIN,":encoding(euc-jp)"; $string = <>; print "is_utf8: ", Encode::is_utf8($string) ? 1 : 0, "\n"; # perl is_utf8.pl < euc_jp.txt is_utf8: 0 is_utf8: 1
UTF-8 フラグの ON,OFF には次の関数を使用します.
perl5.8 では文字コード自動判別も用意されており,Encode::Guess がそれです.
use Encode::Guess qw/euc-jp shiftjis 7bit-jis/; my $data; $data .= $_ while (<>); my $enc = guess_encoding($data); ref($enc) or die "Can't guess: $enc"; print "encoding is ", $enc->name, "\n";
Encode::Guess に引数で文字コードからなる配列を渡せば,その中から文字 コードを推定します(デフォルトで ascii,utf8 および BOM 付きの UTF-16/32 が含まれています).guess_encoding() で推定し,$enc に戻り値が返ります. 一意に文字コードが推定できれば $enc にはオブジェクトへのリファレンスが返り ますが,2つ以上の文字コードの候補がある場合や1つも推定できない時には エラーメッセージが $enc に収納され,ref($enc) は偽になります.
Encode::decode でも文字コードを Guess と指定すること で,自動判別ができます.ただしこの場合でも use Encode::Guess をして, Encode::Guess->set_suspects で候補となる文字コードを指定する必要があり ます.
# 文字コードを euc-jp に変換 use Encode; use Encode::Guess; binmode STDOUT,":encoding(euc-jp)"; my $data; $data .= $_ while (<>); Encode::Guess->set_suspects(qw/euc-jp shiftjis 7bit-jis/); my $enc_japan = Encode::decode("Guess", $data); print $enc_japan;
詳しくは perldoc Encode::Guess を御覧下さい.
perl の Unicode サポートの基となっている Unicode の規格ですが,現在 のバージョンは Unicode 4.1.0 となっています.Unicode では膨大な数から なる文字を扱いやすいように,多くの文字集合を定義しています.perl でも 同様に文字集合を扱うことが出来ます.
文字集合をどのような観点から定義するかにより大きく3つに分類できます.
Unicode Character Database では,個々の文字についていろいろな視点から見た Property を付与し文字を分類しています.例えば,大文字,小文字,数字,記 号,空白,文字を並べる方向,他の文字と組み合わせられるかなどです.ここ では General Category Values というものを見ていきます.
短縮形 | Property名 | 説明 |
---|---|---|
L | Letter | Letter |
Lu | UppercaseLetter | Letter, Uppercase |
Ll | LowercaseLetter | Letter, Lowercase |
Lt | TitlecaseLetter | Letter, Titlecase |
Lm | ModifierLetter | Letter, Modifier |
Lo | OtherLetter | Letter, Other |
M | Mark | Mark |
Mn | NonspacingMark | Mark, Non-Spacing |
Mc | SpacingMark | Mark, Spacing Combining |
Me | EnclosingMark | Mark, Enclosing |
N | Number | Number |
Nd | DecimalNumber | Number, Decimal |
Nl | LetterNumber | Number, Letter |
No | OtherNumber | Number, Other |
P | Punctuation | Punctuation |
Pc | ConnectorPunctuation | Punctuation, Connector |
Pd | DashPunctuation | Punctuation, Dash |
Ps | OpenPunctuation | Punctuation, Open |
Pe | ClosePunctuation | Punctuation, Close |
Pi | InitialPunctuation | Punctuation, Initial quote |
Pf | FinalPunctuation | Punctuation, Final quote |
Po | OtherPunctuation | Punctuation, Other |
S | Symbol | Symbol, Math |
Sm | MathSymbol | Symbol, Math |
Sc | CurrencySymbol | Symbol, Currency |
Sk | ModifierSymbol | Symbol, Modifier |
So | OtherSymbol | Symbol, Other |
Z | Separator | Separator |
Zs | SpaceSeparator | Separator, Space |
Zl | LineSeparator | Separator, Line |
Zp | ParagraphSeparator | Separator, Paragraph |
C | Other | Other |
Cc | Control | Other, Control |
Cf | Format | Other, Format |
Cs | Surrogate | Other, Surrogate(使用不可) |
Co | PrivateUse | Other, Private Use |
Cn | Unassigned | Other, Not Assigned |
perl では,これらの Property を持つ文字を \p{} と いう書式でまとめて表現することが出来ます.例えば m/\p{S}/ とすれば,記号のプロパティが付与されている文 字のマッチングが行なえます.この書式の否定形は \P{} もしくは \p{^ } です.
他にも多くのPropertyが ありますので,詳しくは UNICODE CAHACTER DATABASE を御覧下さい.
perl5.6 との後方互換性のために, property 名に Is がついた書式も残されています.例えば \p{IsLu} は \p{Lu} と同義です.
Unicode では世界中の言語で用いられる文字を定義しているわけですが,言 語ごとに文字集合を定義しているわけではなく,ある言語から派生した言語や, 歴史的に同じ祖先である言語は,統一されて定義されています.日本語,中国 語,韓国語で使用される漢字が統一されてしまっているのはおなじみですね. これを Unicode では Script と呼んでいます. Supported Scripts で,定義されている Script を見ることができます.インド・ヨー ロッパ語族の多くは Latin と1つにまとめられているのです が,日本語に関してみてみると, Katakana, Hiragana ,そして漢字である Han と3つの Script から成立しています.
さて,これらの Script も Property と同じ で,\p{} を用いて \p{Katakana} のように表現することが出来ます.Script と実際のコードポイントとの対応は http://www.unicode.org/Public/UNIDATA/Scripts.txt で見ることができ ます.Hiragana,Katakana,Han(漢字)に対応するコードポイントは以下のよ うになっています.
Script名 | コードポイント | 説明 |
---|---|---|
Hiragana | 3041..3096 | ぁ..ヶ |
309D..309E | ゝゞ | |
309F | HIRAGANA DIGRAPH YORI | |
Katakana | 30A1..30FA | ァ..ヲ゛ |
30FD..30FE | ヽヾ | |
30FF | KATAKANA DIGRAPH KOTO | |
31F0..31FF | (小文字の)クシストヌハヒフヘホムラリルレロ | |
FF66..FF6F | 半角ヲと半角カナ小文字 | |
FF71..FF9D | 半角カナ ア..ン | |
Han | 2E80..2E99 | CJK部首補助[26] |
2E9B..2EF3 | CJK部首補助[89] | |
2F00..2FD5 | 康熈部首[214] | |
3005 | 々 | |
3007 | 〇 | |
3021..3029 | HANGZHOU NUMERAL ONE..NINE[9] | |
3038..303A | HANGZHOU NUMERAL TEN..THIRTY[3] | |
303B | VERTICAL IDEOGRAPHIC ITERATION MARK | |
3400..4DB5 | CJK統合漢字[6582] | |
4E00..9FA5 | CJK統合漢字[20902] | |
F900..FA2D | CJK互換漢字[302] | |
FA30..FA6A | CJK互換漢字[59] | |
20000..2A6D6 | CJK統合漢字[42711] | |
2F800..2FA1D | CJK互換漢字[542] |
Hanは全部で71442文字あります.
Unicode では,Script に加えて文字集合として Block を定義しています. Script と Block の違いは,Script の概念は自然言語に基づいてグルーピングさ れていることに対して,Block は256文字を基準とした人工的な文字集合に基づ いており,コードポイントが連続しています.実際のコードポイントと Block の対応は, http://www.unicode.org/Public/UNIDATA/Blocks.txt にあります.またブ ロックごとに字形を掲載した PDF ファイルが Code Charts にあります.
日本語に関係する Block をいくつか掲載します.
コードポイント | Block名 | 説明 |
---|---|---|
3000..303F | InCJKSymbolsAndPunctuation | 全角の記号.、。〃?々〆〇〈〉《》「」『』【】〒〓〔〕〜 など |
3040..309F | InHiragana | ひらがな |
30A0..30FF | InKatakana | カタカナ(・,ーも含まれます). Scriptと異なり半角カナは 含まれません. |
31F0..31FF | InKatakanaPhoneticExtensions | カタカナ表音拡張.(小文字の)クシストヌハヒフヘホムラリルレロ |
4E00..9FFF | InCJKUnifiedIdeographs | CJK統合漢字.いわゆる漢字です.JIS X 208基本漢字とJIS X 0212拡 張漢字も含まれています. |
FF00..FFEF | InHalfwidthAndFullwidthForms | 全角文字と半角カナなど.!#$%&()*+,./ 0-9:;<=>?@A-Z[]^_`a-z{|} ̄¥など |
perl で用いられる Block 名は Unicode で使われている Block 名に In がついた形となっています.この In は Script や Property 名と衝突しない場合は省略可能ですが, Katakana や Hiragana のように衝突してしまうことがあるので常に In をつけておいた方が無難です(したがって \p{Katakana} と \p{InKatakana} は意味が異なります).
perlで定義されている Script や Property や Block 名を組み合わせたり, 独自にコード範囲を設定することにより 新たに1つの Property を作成することができます.新たにサブルーチンを定 義することで,正規表現の\p{}や\P{}で使うことができます。
以下はひらがなとカタカナにマッチするPropertyです
sub InKana { return <<'END'; +utf8::InHiragana +utf8::InKatakana END } print $_ for $string =~ /(\p{InKana})/g;
\p{Han}も追加すれば,記号を除いた日本語の文字列にマッチする正規表現 が書けます.
sub InJapanese { return <<'END'; +utf8::InHiragana +utf8::InKatakana +utf8::Han END } print $_,"\n" for $string =~ /(\p{InJapanese}+)/g;
IME などで入力できない文字は \x{文字コード} で直接表すことが出来ます.文字コードは UCS2 を使用します. 例えばひらがなの あ は,次のようになります.
$a = "\x{3042}"
またchr関数を使っても表すことができます.
$a = chr(0x3042)
use charnames ':full' を宣言すれば, \N{} を用いて Unicode character name で表現できます.
use charnames ':full'; $a = "\N{HIRAGANA LETTER A}";
以上の文字集合を確かめるための簡単なスクリプトを紹介します. UCS2 の範囲で各 property にマッチする文字を出力するものです.
#!/usr/bin/perl binmode STDOUT,":utf8"; foreach my $code ( 1..65535 ) { my $char = pack "U", $code; my $ucs2 = sprintf "%04x", $code; if ( $char =~ /\p{InHiragana}/ ){ print "$ucs2\t$char\n"; } }
\p{InHiragana} の部分を書き換えれば,いろいろな文字 集合を確認できます.
最後に気をつけなければならないのは,対応している Unicode のバージョ ンです.perl5.8.0 では Unicode 3.2 でしたが,perl5.8.1 では Unicode 4.0.0に,perl5.8.4では Unicode 4.0.1 に,perl5.8.7からは最新である Unicode 4.1.0に変更されています.おそらく日本語関係の部分ではそれほど 変更はないと思いますが,一応頭に入れておいたほうがいいかもしれません.
ここでは,perl5.8で追加された機能ではないですが,Unicode の正規化に ついて説明します.正規化については Unicode Standard Annex #15 Unicode Normalization Formsに記載されています. 日本語では貞廣さんによる Unicode正規化とはで詳しく説明されています.
Unicode では2つの分解を定義しています.標準分解(canonical decomposition)と互換性分解(compatibility decomposition)です. 分解は Character Names List に基づいて行なわれます.
標準分解
互換性分解
分解の種類と合成の有無により計4種類の正規形が存在します.
正規形 | 略称 | 説明 |
---|---|---|
正規形D (Normalization Form D) |
NFD | 標準分解 |
正規形C (Normalization Form C) |
NFC | 標準分解の後,合成 |
正規形KD (Normalization Form KD) |
NFKD | 互換性分解 |
正規形KC (Normalization Form KC) |
NFKC | 互換性分解の後,合成 |
正規化によってどの文字がどう変換されるかは Normalization Charts を御覧ください(Katakana あたりがおもしろい)
Perl で Unicode 正規化を実現するには,モジュール Unicode::Normalize を使用します.正規化の略称と同じ名前の関数が用意されています.詳しい使い方は 作者の man ページを御覧ください.
以下は簡単なサンプルコードです
#!/usr/bin/env perl use Unicode::Normalize; binmode STDOUT,":utf8"; my $raw_string = "\x{FF8A}\x{FF9F}"; # 半角の パ my $string_NFD = NFD($raw_string); # 正規形 D my $string_NFC = NFC($raw_string); # 正規形 C my $string_NFKD = NFKD($raw_string); # 正規形 KD my $string_NFKC = NFKC($raw_string); # 正規形 KC output($raw_string,'RAW'); output($string_NFD,'NFD'); output($string_NFC,'NFC'); output($string_NFKD,'NFKD'); output($string_NFKC,'NFKC'); sub output { my ($string, $format) = @_; print $format, ":\t", $string, "\t"; my @codes = map { sprintf("%04x", unpack("U", $_)) } split(//, $string); print join(',', @codes), "\n"; }
出力結果
RAW: パ ff8a,ff9f NFD: パ ff8a,ff9f NFC: パ ff8a,ff9f NFKD: パ 30cf,309a, NFKC: パ 30d1
Canonical | Alias | euc-jp | /euc.*jp/i /jp.*euc/i /ujis/i |
---|---|
shiftjis | /shift.*jis/i /sjis/i |
cp932 | なし |
7bit-jis | /jis/i |
utf8 | /utf-?8/i |
Up(P): Home
間違い,勘違い,スペルミスなどは
まで
Last modified: Thu Mar 16 04:31:47 JST 2006