NAME

encoding - 非 ascii や非 utf8 でスクリプトを書けるようにする

SYNOPSIS

  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';

ABSTRACT

ちょっとした歴史から始めましょう: Perl 5.6.0 で Unicode サポートが 導入されました。 substr() や正規表現を複雑な CJK 文字に適用することができるように なりました -- スクリプトが UTF-8 で書かれていれば。 しかし以前は、UTF-8 をサポートしたテキストエディタはあまり存在していなくて、 多くのユーザは従来のエンコーディングでスクリプトを書いてしまって Perl5.6 の新機能全体を使うことをあきらめていました。

未来へ話を戻しましょう: perl 5.8.0 で encoding プラグマが導入され、 (Encode モジュールが対応していれば)任意のエンコーディングで スクリプトを書けるようになり、Unicode サポートも以前同様に 使うことができます。 このプラグマは以下に挙げるようなことをすることで達成されます:

リテラル変換

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 には 影響を与えません。

5.8.1 が必要な機能

本プラグマで提供される機能の幾つかは perl 5.8.1 を要求します。 これらの機能の大部分は Inaba Hiroto により行われました。 その他の機能と変更点は 5.8.0 で使えます。

「非 EUC」2 バイトエンコーディング

perl はこのプラグマを適用する前にスクリプトを解析する必要があるので、 Shift_JIS や Big-5 のような、2 バイト目に'\'(バックスラッシュ; \x5c)を含む 可能性があるエンコーディングで失敗していました。 なぜなら、二バイト目が誤って後続するクォート文字をエスケープしてしまう 可能性があるからです。 Perl 5.8.1 以降ではこの問題は解決されました。

tr//

tr// は perl 5.8.0 リリースのときには Perl 5 porters が見逃して しまっていました。 詳しくは後述のセクションを参照してください。

DATA 疑似ファイルハンドル

もう一つ見逃されていた機能は DATA です。

使用法

use encoding [ENCNAME] ;

スクリプトのエンコーディングを ENCNAME に設定します。 ${^UNICODE} が存在していてそれが非ゼロでない限り、STDIN および STDOUT の PerlIO 層は ":encoding(ENCNAME)" に設定されます。

STDERR は変更されないことに注意してください。

同様に、非 STD ファイルハンドルも影響を受けないことに注意してください。 これらの層を変更するには use open または binmode を使います。

エンコーディングが指定されていない場合、環境変数 PERL_ENCODING が 参照されます。 エンコーディングが見つからなかった場合には、 Unknown encoding 'ENCNAME' というエラーになります。

use encoding ENCNAME [ STDIN => ENCNAME_IN ...] ;

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 以降)。

use encoding ENCNAME Filter=>1;

これはエンコーディングプラグラマをソースフィルタに適用します。 デフォルトのアプローチが(qq()やqr()中で) 変数展開されたリテラルを デコードするだけなのに対して、本プラグマはソースコード全体に ソースフィルタを適用します。 詳しくは後述する "The Filter Option" を参照してください。

no encoding;

スクリプトエンコーディングを解除します。 STDIN、STDOUT の層は ":raw" (デフォルトの、バイトの生ストリームを 処理しない)にリセットされます。

Filter オプション

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 で記述されている 場合には、全く安定していません。

警告

スコープではありません

このプラグマはスクリプト毎のものであってブロックレキシカル毎では ありません。 最後に現れた 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{....} と 書かなくてもすむということです。 だから、希望するエンコーディングで文字列や正規表現を自由に書いて 構いません。

tr/// の範囲指定

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で修正されました。

tr///; の回避策

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ではこれは修正されています。

例 - Greekperl

    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 バイトを超える正規表現中のリテラル

ネイティブのマルチバイトエンコーディング(固定長であれ可変長であれ)に対して、 現在の正規表現の実装は 127 バイトを超える正規表現リテラルに対して エラーを発生します。

EBCDIC

エンコーディングプラグマは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 のロジック

:localeのロジックは以下の通りです:

  1. プラットフォームが langinfo(CODESET) インターフェースに 対応していれば、それが返すコードセットが open プラグマの デフォルトエンコーディングとして使用されます。

  2. 1. が成り立たないが、locale プラグマが有効の場合、環境変数 LC_ALL と LANG が(この順番で検索されます)エンコーディング(もしあれば . の後の部分)と マッチしてそれが見つかれば、open プラグマのデフォルトエンコーディングとして 使用されます。

  3. 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 で 実装されました。

SEE ALSO

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