Perl

  • Perl 5.8.8 からは UTF-8 が完全標準になった。use utf8 を書かなくてもソースは UTF-8 とみなされるようになった。
  • Perl 5.6 以降では UTF-8 が標準文字コードとなった。
    • Perl 5.6 の Unicode 対応はバグ多し。使うなら 5.8 以降で。
  • Perl 5.8 以降は標準モジュールの Encode モジュールで文字コード変換を行う。
  • 自動判定したい場合は、Jcode.pm を使う。(Encode::Guess は望ましい動作をしないようだ…)
  • Perl 5.8 以前の場合は Jcode.pm で文字コード変換を行う。
    • (Jcode.pm すら使えない環境は古すぎるので無視でよいと思う。)
  • Perl 5.8.8 より前は、UTF-8 でソースを書いた場合、use utf8 ディレクティブを指定する必要がある。
    • use utf8 を書かなかった場合、ソースはあくまで単純なバイト列として扱われる。(UTF-8自体は use utf8 の指定がなくても使えることに注意。)
  • UTF-8になったとはいえ、内部表現はあくまでバイト配列である。このため、他の文字コード体系でも普通の変数に代入できてしまう。変数の使い分けに注意が必要。
  • 変数は UTF8 フラグを持つ。
    • UTF8フラグが設定されていると、その変数は UTF8 文字列として扱われる。(例えば、length は文字数を返す。)
    • そうでなければバイト列として取り扱われる。(例えば、length はバイト数を返す。)
    • utf8::is_utf8($str) で UTF8 フラグが立っているかどうかがわかる。
  • U+0000〜U+00FF までを取り扱うのと、UTF8フラグの有無があるため、バイト列との変換は4通り存在する。
変換関数\変換前"\xA1" (バイト列)"\xC2\xA1" (バイト列)"\xC2\xA1" (UTF8)
utf8::upgrade($str)"\xC2\xA1" (UTF8)"\xC3\x82\xC2\xA1" (UTF8)"\xC2\xA1" (UTF8)
utf8::downgrade($str)"\xA1" (バイト列)"\xC2\xA1" (バイト列)"\xA1" (バイト列)
utf8::encode($str)"\xC2\xA1" (バイト列)"\xC3\x82\xC2\xA1" (バイト列)"\xC2\xA1" (バイト列)
utf8::decode($str)"\xA1" (バイト列)"\xC2\xA1" (UTF8)"\xC2\xA1" (UTF8)

このとき、$str 自体が書き換えられる点に注意。 $r = utf8::encode($s) とやっても $r は空になるだけ。

  • 内部のバイト列の16進表記を得るには unpack("H*", $str) を使うこと。それ以外(substr で取り出して ord とか)は文字コード変換が入るのでダメ。
  • Perl 5.8.0 では、環境変数 LC_ALL などに UTF8 などが設定されていると、暗黙で入出力のテキストモードに UTF8 が適用される。
    • Perl 5.8.1 以降では、この挙動は取りやめになった。(危険すぎ)
  • Encode モジュールの変換表は UnicodeConsortium のものを使用している。
    • UnicodeConsortium の変換表は JIS に適合せず、Windows, Mac の変換表とも異なる。
    • 信用できるのは結局実装なので、Windows の変換表を作るなら、Windows API で変換してみる必要がある。

Perlが対応している日本語文字コードは

Encode::JP に一覧が書かれている。

一般に使うのは

  • euc-jp
  • shiftjis
    • MacJapanese
    • cp932
  • iso-2022-jp
  • iso-2022-jp-1

特定の文字コード間で変換するには

詳しくは Encode, Encode::JP を参照。

常にutf8を経由する点に注意。

use Encode;
$stream = encode(文字コード指定文字列, $utf8);
$utf8 = decode(文字コード指定文字列, $stream);
$sjis = encode("shiftjis", $utf8);
$eucjp = encode("euc-jp", $utf8);
$iso2022jp = encode("iso-2022-jp", $utf8);
$iso2022jp1 = encode("iso-2022-jp-1", $utf8);
$utf8 = decode("shiftjis", $sjis);
$utf8 = decode("euc-jp", $eucjp);
$utf8 = decode("iso-2022-jp", $iso2022jp);
$utf8 = decode("iso-2022-jp-1", $iso2022jp1);

文字コードを自動判別させるには

Jcode.pm を使う場合

use Jcode;
my $utf8 = jcode($str)->utf8;
my $sjis = jcode($str)->sjis;
my $eucjp = jcode($str)->eucjp;
my $jis = jcode($str)->jis;

Encode::Guess を使う場合

詳しくは Encode::Guess を参照。

エラーが出ても無視して変換する場合

use Encode;
use Encode::Guess qw/euc-jp shiftjis 7bit-jis/;

my $utf8 = decode("Guess", $data);

エラーを検出する場合

use Encode::Guess;

my $enc = guess_encoding($str, qw/euc-jp shiftjis 7bit-jis/);
ref($enc) or die "Can't guess: $enc";
my $utf8 = $enc->decode($data);

Encode::Guess を使うと shiftjis or euc-jp, euc-jp or utf8 が出る。

■症状

以下のようなメッセージが出る。

shiftjis or euc-jp at /usr/local/lib/perl5/5.8.7/mach/Encode.pm line 166

■原因

Encode::Guess は、decode に失敗したものをリストから取り除くという動きをする。

decode できたものが複数だった場合、リファレンスではなく、decode に成功した方式の文字列を返す。("shiftjis or euc-jp", "euc-jp or utf8" など)

Jcode.pm の場合は、変換できた率が高いものが採用される。(その分変換は遅い。)

■参考

http://donzoko.net/cgi-bin/tdiary/20040118.html

■対応

1.エラー検出方式を使うようにして、優先度をつけるなどする。

2.やっぱり Jcode.pm を使う。:(

ファイルハンドルの文字コードを指定するには

open か binmode を使う。一般には、open を使う。 (途中で文字コードを変更することは少ないはず。)

open FILEHANDLE, "<:encoding(shiftjis)", $filename;
open FILEHANDLE, "<:utf8", $filename;
binmode(FILEHANDLE, ":encoding(shiftjis)");
binmode(FILEHANDLE, ":utf8");

文字コードを指定した場合は、テキストモードとして扱われて、自動的に UTF8 フラグが設定される。

文字コードを指定しなかった場合は、バイナリモードとして扱われて、UTF8 フラグは付かない。

標準入出力の文字コードを指定するには

詳しくは perl の pragmas encoding を参照。

EUC-JP で入出力する。

use encoding "euc-jp";

EUC-JP で出力する。入力のみ ShiftJIS とする。

use encoding "euc-jp", STDIN=>"shiftjis";

utf8 のまま入出力したい場合も指定する必要がある。指定しないと出力時に警告が出る。

use encoding "utf8";

警告は以下のように出る。

Wide character in print at hello.pl line 5.

use encoding は純粋に標準入出力にのみ影響する。 そのため、while (<>) を使って標準入力兼ファイル入力ということができない。

ActivePerl で日本語を手入力した場合の挙動

sub dump_string
{
  my ($s) = @_;
  for ($i = 0; $i < length($s); $i++)
  {
    printf("%04x ", ord(substr($s, $i, 1)));
  }
  print "\n";
}

while (<>)
{
  dump_string($_);  
}

use encoding を使わない場合

test
0074 0065 0073 0074 000a
テスト
0083 0065 0083 0058 0083 0067 000a

バイトで処理されている。 改行コードは LF のみ。

use encoding "shiftjis"; の場合

test
0074 0065 0073 0074 000d 000a
テスト
30c6 30b9 30c8 000d 000a

Unicode(UCS-2)に変換されている。 改行コードをご丁寧に CR+LF にしてくれるので要注意。 chomp そのままでは LF のみ取り除いてしまいます。$/ の設定が必要。

参考

http://www.lr.pi.titech.ac.jp/~abekawa/perl/perl_unicode.html


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-10-29 (月) 17:05:59 (3615d)