encoding - 非 ascii や非 utf8 でスクリプトを書けるようにする
use encoding "greek"; # Perl like Greek to you?
use encoding "euc-jp"; # Jperl!
# ネイティブエンコーディングをシェルが対応しているのなら以下のようにも書ける
perl -Mencoding=latin2 -e '...' # Feeling centrally European?
perl -Mencoding=euc-kr -e '...' # Or Korean?
# more control
# 単純な euc-cn → utf-8 コンバータ
use encoding "euc-cn", STDOUT => "utf8"; while(<>){print};
# "no encoding;" もサポートされている (しかしスコープはありません!)
no encoding;
# 別のやり方、Filter
use encoding "euc-jp", Filter=>1;
# これで漢字の識別子が使えます -- euc-jp で!
# switch on locale -
# note that this probably means that unless you have a complete control
# over the environments the application is ever going to be run, you should
# NOT use the feature of encoding pragma allowing you to write your script
# in any recognized encoding because changing locale settings will wreck
# the script; you can of course still use the other features of the pragma.
use encoding ':locale';
ちょっとした歴史から始めましょう: Perl 5.6.0 で Unicode サポートが 導入されました。 substr()
や正規表現を複雑な CJK 文字に適用することができるように なりました -- スクリプトが UTF-8 で書かれていれば。 しかし以前は、UTF-8 をサポートしたテキストエディタはあまり存在していなくて、 多くのユーザは従来のエンコーディングでスクリプトを書いてしまって Perl5.6 の新機能全体を使うことをあきらめていました。
未来へ話を戻しましょう: perl 5.8.0 で encoding プラグマが導入され、 (Encode
モジュールが対応していれば)任意のエンコーディングで スクリプトを書けるようになり、Unicode サポートも以前同様に 使うことができます。 このプラグマは以下に挙げるようなことをすることで達成されます:
すべてのリテラル(q//,qq//,qr//,qw///, qx//
)を内部的に指定された エンコーディングから utf8 に変換します。 Perl 5.8.1 以降では、tr///
のリテラルと擬似ファイルハンドル DATA
も 同様に変換されます。
STDIN
と STDOUT
の PerlIO 層を指定されたエンコーディングに変更します。
EUC-JP で次のようにコードを書くことができます:
my $Rakuda = "\xF1\xD1\xF1\xCC"; # 駱駝
#<-char-><-char-> # 4 オクテット
s/\bCamel\b/$Rakuda/;
use encoding "euc-jp"
が有効の場合、これは UTF-8 で 次のように書いたコードと同じです:
my $Rakuda = "\x{99F1}\x{99DD}"; # 二つの Unicode 文字
s/\bCamel\b/$Rakuda/;
STD(IN|OUT)
のための PerlIO 層encoding プラグマはまた、STDIN および STDOUT のファイルハンドル層を、 指定されたエンコーディングに変更します。 したがって、
use encoding "euc-jp";
my $message = "Camel is the symbol of perl.\n";
my $Rakuda = "\xF1\xD1\xF1\xCC"; # Camel in Kanji
$message =~ s/\bCamel\b/$Rakuda/;
print $message;
これは "\x{99F1}\x{99DD} is the symbol of perl.\n" ではなく "\xF1\xD1\xF1\xCC is the symbol of perl.\n" を出力します。
追加の引数を与えることによってこれをオーバーライドできます。 以下を参照してください。
デフォルトでは、バイトセマンティクスで操作している文字列と Unicode 文字データの文字列を連結すると、新しい文字列は バイト文字列を ISO 8859-1 (Latin-1) としてデコードしたものから 作られます。
encoding プラグマは、代わりに指定されたエンコーディングを使うことで これを変更します。 例えば:
use encoding 'utf8';
my $string = chr(20000); # a Unicode string
utf8::encode($string); # now it's a UTF-8 encoded byte string
# concatenate with another Unicode string
print length($string . chr(20000));
これは 2
を表示します; なぜなら $string
は UTF-8 に 昇格されるからです。 use encoding 'utf8';
がない場合、代わりに 4
を表示します; なぜなら $string
は Latin-1 として解釈されると 3 オクテットだからです。
スコープ内に encoding
プラグマがある場合、 返される長さは Unicode 文字での $/
の長さから計算され、 これはネイティブエンコーディングでの $/
の長さと常に同じとは 限りません。
このプラグマは utf8::upgrade に影響を与えますが、utf8::downgrade には 影響を与えません。
本プラグマで提供される機能の幾つかは perl 5.8.1 を要求します。 これらの機能の大部分は Inaba Hiroto により行われました。 その他の機能と変更点は 5.8.0 で使えます。
perl はこのプラグマを適用する前にスクリプトを解析する必要があるので、 Shift_JIS や Big-5 のような、2 バイト目に'\'(バックスラッシュ; \x5c)を含む 可能性があるエンコーディングで失敗していました。 なぜなら、二バイト目が誤って後続するクォート文字をエスケープしてしまう 可能性があるからです。 Perl 5.8.1 以降ではこの問題は解決されました。
tr//
は perl 5.8.0 リリースのときには Perl 5 porters が見逃して しまっていました。 詳しくは後述のセクションを参照してください。
もう一つ見逃されていた機能は DATA
です。
スクリプトのエンコーディングを ENCNAME に設定します。 ${^UNICODE} が存在していてそれが非ゼロでない限り、STDIN および STDOUT の PerlIO 層は ":encoding(ENCNAME)" に設定されます。
STDERR は変更されないことに注意してください。
同様に、非 STD ファイルハンドルも影響を受けないことに注意してください。 これらの層を変更するには use open
または binmode
を使います。
エンコーディングが指定されていない場合、環境変数 PERL_ENCODING が 参照されます。 エンコーディングが見つからなかった場合には、 Unknown encoding 'ENCNAME'
というエラーになります。
STDIN => ENCNAME
形式を使うことによって、STDIN と STDOUT の エンコーディングを独立に設定できます。 この場合、最初の ENCNAME を省略することはできません。 STDIN => undef
は入出力の変換(transcoding)を完全にオフにします。
${^UNICODE} が存在していており、かつそれが非ゼロであった場合、これらの オプションは完全に無視されます。 $<^UNICDOE>は perl 5.8.1 で導入された変数です。 perlrun, "${^UNICODE}" in perlvar, "-C" in perlrun を 参照してください (perl 5.8.1 以降)。
これはエンコーディングプラグラマをソースフィルタに適用します。 デフォルトのアプローチが(qq()やqr()中で) 変数展開されたリテラルを デコードするだけなのに対して、本プラグマはソースコード全体に ソースフィルタを適用します。 詳しくは後述する "The Filter Option" を参照してください。
スクリプトエンコーディングを解除します。 STDIN、STDOUT の層は ":raw" (デフォルトの、バイトの生ストリームを 処理しない)にリセットされます。
use encoding
の魔法は識別子には適用されません。 ${"\x{4eba}"}++
(漢字一文字の'人'、$human++)が動作するようにするには、 UTF-8でスクリプトを記述する必要があります -- あるいはソースフィルタを 使います。 つまり 'Filter=>1' とします。
これは何を意味するのでしょう? ソースコードは 'use utf8' を指定して UTF-8で書いたかのように振る舞います。 だから使っているエディタがたとえば Shift_JIS しかサポートしていなくても、 Programming Perl, 3rd Ed.
の第 15 章にある例を試すことがでます。 たとえば、UTF-8 識別子を使えます。
このオプションは非常に遅く、(これを書いている時点では) ASCII でない 識別子は、このオプション抜きでかつソースコードが UTF-8 で記述されている 場合には、全く安定していません。
現在、Filter オプションは非フィルタオプションのように STDIN および STDOUT を 設定します。 そして STDIN=>ENCODING
や STDOUT=>ENCODING
は 非フィルタ版と同様に動作します。
use utf8
は暗黙に宣言されるので、 ${"\x{4eba}"}++
とするために use utf8
とする必要はもはやありません。
このプラグマはスクリプト毎のものであってブロックレキシカル毎では ありません。 最後に現れた use encoding
もしくは no encoding
だけが意味を持ち、 スクリプト全体に影響を及ぼします。 しかしながら、no encoding プラグマはサポートされ、 use encoding はスクリプトの中で何回現れてもかまいません。 このプラグマを複数回使用することは非推奨です。
同じ理由で、本プラグマをモジュールの中で使うことも非推奨です (しかし、上述の場合ほど強い非推奨ではありません。以降を参照してください)。
それでもこのプラグマを使ったモジュールを書く必要があるのなら、ロードされる 順番に十分に注意してください。 以下のコードを見てみましょう;
# called module
package Module_IN_BAR;
use encoding "bar";
# stuff in "bar" encoding here
1;
# caller script
use encoding "foo"
use Module_IN_BAR;
# surprise! use encoding "bar" is in effect.
この現象を避ける最善の方法は他のモジュールをロードした後でこのプラグマを 使うというものです。 例:
use Module_IN_BAR;
use encoding "foo";
従来の文字位置を持っているリテラル(文字列もしくは正規表現)のみが 影響されるということに注意してください: 次のように書いた場合
\xDF\x{100}
このデータはネイティブエンコーディングではなく(Latin 1 と)Unicode で あるとみなされます。 言い換えると、以下の例は "greek" でマッチします:
"\xDF" =~ /\x{3af}/
しかし次の例では違います
"\xDF\x{100}" =~ /\x{3af}\x{100}/
なぜならば左辺にある \xDF
(ISO 8859-7 GREEK SMALL LETTER IOTA WITH TONOS) は、同じく左辺に \x{100}
があるために \x{3af}
に 昇格されない からです。 従来のデータと Unicode を同じ文字列の中で混ぜるべきではありません。
本プラグマは 0x80 から 0xFF の範囲の文字位置のエンコーディングにも 影響します: この範囲にある通常の文字は (UTF-8 エンコードが必要となる 0x100 以上の文字と組み合わされていない限り) 8 ビットバイトで あるかのように放っておかれますが、 もし encoding
プラグマが使われているなら、0x80 から 0xFF の 範囲であっても UTF-8 エンコードされます。
以上のことを踏まえて、このプラグマに関して最も良いことは、 ネイティブエンコーディングであなたの名前を書くのに \x{....} と 書かなくてもすむということです。 だから、希望するエンコーディングで文字列や正規表現を自由に書いて 構いません。
encoding プラグマは q//,qq//,qr//,qw///, qx//
中の文字列リテラルを デコードすることによって動作します。 perl 5.8.0 では、これは tr///
には適用されていませんでした。 このため、
use encoding 'euc-jp';
#....
$kana =~ tr/\xA4\xA1-\xA4\xF3/\xA5\xA1-\xA5\xF3/;
# -------- -------- -------- --------
これは次の例のようには動作しませんでした
$kana =~ tr/\x{3041}-\x{3093}/\x{30a1}-\x{30f3}/;
utf8 euc-jp charnames::viacode()
-----------------------------------------
\x{3041} \xA4\xA1 HIRAGANA LETTER SMALL A
\x{3093} \xA4\xF3 HIRAGANA LETTER N
\x{30a1} \xA5\xA1 KATAKANA LETTER SMALL A
\x{30f3} \xA5\xF3 KATAKANA LETTER N
この非直感的な動作は perl 5.8.1で修正されました。
perl 5.8.0 では、以下のような回避策がありました。
use encoding 'euc-jp';
# ....
eval qq{ \$kana =~ tr/\xA4\xA1-\xA4\xF3/\xA5\xA1-\xA5\xF3/ };
tr//
式が qq{}
に囲まれている点に注意してください。 このアイデアは tr///
に '展開'(interpolate)させる古典的なイディオムと 同じです。
tr/$from/$to/; # wrong!
eval qq{ tr/$from/$to/ }; # workaround.
いずれにしろ、encoding プラグマは q//
の場合であっても 影響を及ぼすので、tr///
は Perl 5 Porters の目にはデコードすることが 明らかなものとして写りませんでした。 5.8.1 以降のPerlではこれは修正されています。
use encoding "iso 8859-7";
# \xDF in ISO 8859-7 (Greek) is \x{3af} in Unicode.
$a = "\xDF";
$b = "\x{100}";
printf "%#x\n", ord($a); # will print 0x3af, not 0xdf
$c = $a . $b;
# $c will be "\x{3af}\x{100}", not "\x{df}\x{100}".
# chr() is affected, and ...
print "mega\n" if ord(chr(0xdf)) == 0x3af;
# ... ord() is affected by the encoding pragma ...
print "tera\n" if ord(pack("C", 0xdf)) == 0x3af;
# ... as are eq and cmp ...
print "peta\n" if "\x{3af}" eq pack("C", 0xdf);
print "exa\n" if "\x{3af}" cmp pack("C", 0xdf) == 0;
# ... but pack/unpack C are not affected, in case you still
# want to go back to your native encoding
print "zetta\n" if unpack("C", (pack("C", 0xdf))) == 0xdf;
ネイティブのマルチバイトエンコーディング(固定長であれ可変長であれ)に対して、 現在の正規表現の実装は 127 バイトを超える正規表現リテラルに対して エラーを発生します。
エンコーディングプラグマはEBCDICプラットフォームをサポートしていません (この制限を取り払おうとする Porters を歓迎します)。
このプラグマはフォーマットと一緒にはうまく使えません; なぜなら、PerlIO がフォーマットにうまく対処していないからです。 フォーマットが非 ASCII 文字を含んでいた場合おかしな結果となるか、 "wide character warnings" となる。 これを理解するために、次のコードを試してみましょう。
# Save this one in utf8
# replace *non-ascii* with a non-ascii string
my $camel;
format STDOUT =
*non-ascii*@>>>>>>>
$camel
.
$camel = "*non-ascii*";
binmode(STDOUT=>':encoding(utf8)'); # bang!
write; # funny
print $camel, "\n"; # fine
binmode がない場合にはうまくいくように見えますが、binmode がなければ write() の代わりに print() が失敗することになります。
とにかく、Unicode 文字がある場合のフォーマットの使用は、 文字の幅(例: 表意文字の倍幅)や方向(例: アラビア語やヘブライ語のBIDI) を 考えねばならないので、疑わしいです。
use encoding ...
はスレッドセーフではありません (つまり、スレッドを 使うアプリケーションでは使わないでください)。
:localeのロジックは以下の通りです:
プラットフォームが langinfo(CODESET) インターフェースに 対応していれば、それが返すコードセットが open プラグマの デフォルトエンコーディングとして使用されます。
1. が成り立たないが、locale プラグマが有効の場合、環境変数 LC_ALL と LANG が(この順番で検索されます)エンコーディング(もしあれば .
の後の部分)と マッチしてそれが見つかれば、open プラグマのデフォルトエンコーディングとして 使用されます。
1. も2. も失敗したならば、環境変数 LC_ALL と LANG から(この順番で) UTF-8 のような何かを見つけ出そうとし、もし見つかれば :utf8
が open プラグマのデフォルトエンコーディングとして使用されます。
ロケール関係の環境変数(LC_ALL, LC_CTYPE, LANG)が 'UTF-8' もしくは 'UTF8' (大小文字の違いは無視されます)という文字列を 含んでいたならば、STDIN, STDOUT, STDERR および それ以降にオープンされたファイルのエンコーディングは UTF-8 となります。
この本プラグマは Perl 5.8.0 で最初に導入されました。 5.8.1 以降を要求する機能については前のほうで説明しました。
:locale
サブプラグマはバージョン 2.01、つまり Perl 5.8.6 で 実装されました。
perlunicode, Encode, open, Filter::Util::Call,
Ch. 15 of Programming Perl (3rd Edition)
by Larry Wall, Tom Christiansen, Jon Orwant; O'Reilly & Associates; ISBN 0-596-00027-8