Perl プログラミング:
文字エンコーディング
(ビギナー向け)

Naney <>

2014年02月04日

文字エンコーディングの概論

文字集合
(character set)

  • 文字の集合。

符号化文字集合
(coded character set (CCS))

  • 文字集合から整数の集合への写像。
    • Unicode
    • JIS X 0201(ラテン文字 + カタカナなど)
    • JIS X 0208 (漢字・ラテン文字・ひらがななど)

文字符号化方式
(character encoding scheme (CES))

  • 1つ以上の符号化文字集合から、オクテット列への写像。
    • ISO-2022
    • EUC
    • シフト符号化表現
    • UTF-8
  • Unicode ではバイト直列化まで含めたものを CES と呼び、含めない部分を character encoding form (CEF) と呼ぶ。

文字エンコーディング
(character encoding)

  • だいたい CCS + CES のセットのことを言う。
    • ISO-2022-JP
    • EUC-JP
    • Shift_JIS
    • UTF-8
  • HTML5 では Unicode 文字列とバイトストリームの間の変換方法のこと。

Perl における
非 ASCII 文字処理の歴史

オクテット列としての文字列

  • 文字はオクテット列として扱われている。
  • マルチバイト文字列の処理
    • 特に考慮されない。
    • length() ではオクテット数で数えられる。
    • 2オクテット目だからといって特別扱いしない。

jperl 登場

  • Shift_JIS や EUC-JP への限定的な対応をした perl
  • 2オクテット目が \ と同じオクテットだった時の問題への対応。
  • 正規表現や chop などで文字単位で扱う。

jcode.pl

  • 日本語の文字エンコーディングまわりのライブラリ。
  • よく使われていた。

Jcode.pm

  • jcode.pl の後継ライブラリ。
  • 昔書かれていたコードで Jcode を使っているものはまだまだあるので、メンテナンスで目にすることはよくある。

Perl v5.6 系で Unicode 対応開始

  • Unicode サポートが始まった。
  • ただしまだ不完全。
  • Perl v5.8 以降と仕様が違うので、Perl v5.6 系でも動くコードを書くならばバイナリ文字列で扱った方が良い。

Perl v5.8 系で今の形に

  • 今の Unicode 処理が導入された。
  • 以降 Perl v5.8 以降の話

モダン Perl での
文字エンコーディング

2つの文字列

Perl では バイナリ文字列 (binary strings) とテキスト文字列 (text string) がある。

  • バイナリ文字列 (binary strings)
    • オクテット列として扱われる。従来の文字列。
  • テキスト文字列 (text strings)
    • 文字の列として扱われる。
    • マルチバイト文字もきちんと1文字として処理される(length、chomp、正規表現処理など)

UTF8 フラグ

UTF8 フラグ

  • テキスト文字列の内部形式(internal format) が UTF-8 になっているかどうかのフラグ。
  • SV の sv_flags に設定される。

SV

  • perl でのスカラー値の実体。
  • SV を表す STRUCT_SV (sv.h) は以下から構成されている。
ptrtype sv_any; /* ボディへの構造体 */
U32 sv_refcnt;  /* リファレンスカウンタ */
U32 sv_flags;
unison { .. } sv_u;

SVf_UTF8

  • UTF8 フラグ。
  • sv.h で SVf_UTF8 という名前のマクロで定義されている。
  • 値(ビット)は 0x20000000 である。

Devel::Peek で確認する

use utf8;
use warnings;
use strict;
use Devel::Peek;
print Devel::Peek::Dump("");

出力

SV = PV(0x1b03fc0) at 0x1aef0a8
  REFCNT = 1
  FLAGS = (PADTMP,POK,READONLY,pPOK,UTF8)
  PV = 0x1af9420 "\346\204\233"\0 [UTF8 "\x{611b}"]
  CUR = 3
  LEN = 16

use utf8 しても
常に立つわけではない

use utf8;
use warnings;
use strict;
use Devel::Peek;
print Devel::Peek::Dump("a");

出力

SV = PV(0x166fe10) at 0x1690078
  REFCNT = 1
  FLAGS = (PADTMP,POK,READONLY,pPOK)
  PV = 0x169a420 "a"\0
  CUR = 1
  LEN = 16

コーディング

テキスト処理の基本

  • 入力したら decode
    • my $text = Encode::decode("eujp", $input_octets);
  • 処理する
  • 出力時に encode
    • Encode::encode("eucjp", $text);

ファイル読み込み時に勝手に decode

open my $fh "<:encoding(eucjp)", $filename or die $!;

ファイル書き込み時に勝手に encode

open my $fh ">:encoding(eucjp)", $filename or die $!;

PerlIO レイヤーでデフォルト指定

use open ':encoding(eucjp)';

STDIN・STDOUT・STDERR もなら以下。

use open ':encoding(eucjp)';
use open ':std';

開かれているファイルハンドルを binmode で変更

binmode STDOUT, 'encoding(eucjp)';

テキスト処理のポイント

  • 入出力時に decode/encode する。基本テキスト文字列で処理する。
  • 自前で Enocde で変換するか PerlIO レイヤーを活用する。
  • UTF8 フラグが立っていないテキスト文字列があるので is_utf8 で処理を変える時は気をつける。

Appendix

ファイル

by @Naney