前回の
文字化け問題 ── Perlの文字列の実装に迫る
次のテーマは文字列です。文字化けは、
まず、
use strict;
use warnings;
use feature 'say';
use utf8;
use Encode;
use Encode::Locale;
my($name) = @ARGV;
say encode(locale => "こんにちは $name さん");
このスクリプトをhello-mojibake.perl hello-mojibake.
として実行すると、
Perlの文字列 基礎編
実装の詳細に入る前に、decode()
したものはテキスト文字列になります。テキスト文字列をencode()
したものはバイト列になります。utf8プラグマuse utf8;
)decode()
するので、
テキスト文字列とバイト列
ここで、12354
で、"\xe3\x81\x82"
という3バイトのバイト列になります。しかし、length("こんにちは")
は、5
を返しますし、uc("ω")
"Ω"
一方バイト列とは、length()
はバイト列の長さを返し、uc()
はおそらく意味のある値を返しません。
Perlはテキスト文字列をどのように操作すべきかを知っているため、encode()
しなければ、encode()
する必要があります。
文字列を正しく扱う方法
以上を踏まえてプログラマがすべきことをまとめます。外部から来た文字列がテキスト文字列かバイト列かはPerlは知りえないため、decode()
でテキスト文字列であることを確定させます。このとき、encode()
します。
文字化けを修正する
ここで振り返ってリスト7を見ると、encode()
しています。しかし、decode()
すれば、
use strict;
use warnings;
use utf8;
use feature 'say';
use Encode;
use Encode::Locale;
my($name) = map { decode(locale => $_) } @ARGV;
say encode(locale => "こんにちは $name さん");
次のように実行してみましょう。
$ perl hello-ok.pl パール
こんにちは パール さん
今度は正しく表示されました。外部環境からの入力値はdecode()
し、encode()
する。これを守っていれば基本的に文字化けは起きません。しかしながら、encode() / decode()
が必要になることもありえます。そしてそのようなときに、
それでは、
perlの文字列 実装編
さっそくDevel::Peekモジュールで覗いてみましょう。リスト9は各種テキスト文字列をDump()
するものです。
use strict;
use warnings;
use feature 'say';
use Devel::Peek;
use Encode;
$| = 1; # autoflush
my $a = "あ";
say "UTF-8 bytes:";
Dump $a;
# 実行結果(抜粋):
# SV = PV(0x7f9894001e80) at 0x7f989402abf8
# FLAGS = (PADMY,POK,pPOK)
# PV = 0x102202d80 "\343\201\202"\0
say "text string (from utf8):";
Dump decode_utf8($a);
# 実行結果(抜粋):
# SV = PV(0x7f9894001fd0) at 0x7f9894041e98
# FLAGS = (TEMP,POK,pPOK,UTF8)
# PV = 0x1022dee90 "\343\201\202"\0 [UTF8 "\x{3042}"]
さて、"\343\201\202"\0
です。UTF8フラグ付きのテキスト文字列のほうはさらにUnicode表現も出力されていますが、
UTF8フラグ
この2つの文字列の違いはUTF8フラグだけですが、$a eq decode_
は偽になりますし、length()
もバイト列では3を返し、
では、my $a = " あ";
の直後にDump $a . decode_
を実行してみた結果が図3です。まず、"\343\201\202"
というバイナリ表現があり、"\303\243\302\201\302\202"
はもともとの
001: SV = PV(0x7ffe0a802070) at 0x7ffe0a82abe0 002: REFCNT = 1 003: FLAGS = (PADTMP,POK,pPOK,UTF8) 004: PV = 0x10e9d5630 "\303\243\302\201\302\202\343\201\ 202"\0 [UTF8 "\x{e3}\x{81}\x{82}\x{3042}"] 005: CUR = 9 006: LEN = 16
文字列結合演算子の実装
これはperlの実装を見てみましょう。結合演算子はpp_
pp_
という関数です。必要なところだけリスト10に抜粋します。
#line 259 utf8.h
#define DO_UTF8(sv) (SvUTF8(sv) && !IN_BYTES)
#line 237 sv_hot.c
PP(pp_concat)
{
// SV *left, *right, *TARG;
// ...
lbyte = !DO_UTF8(left);
// ...
rbyte = !DO_UTF8(right);
// ...
if (lbyte != rbyte) {
if (lbyte)
#line 288
sv_utf8_upgrade_nomg(TARG);
else {
// ...
}
}
// ...
}
関係ないところは省略していますが、sv_
とここを通る条件です。これは、SvUTF8(sv)
)lbyte !=rbyte
)、if (lbyte) ...
)、TARG
)sv_
という操作をするという意味です
sv_
はPerlからもutf8::upgrade()
という関数で呼ぶことができます
デフォルトエンコーディングとしてのLatin-1
なぜPerlのデフォルトのエンコーディングはLatin-1なのでしょうか。これは、
したがって、decode()
せずにテキスト文字列として扱う場合、