というわけで解説。
2013/03/04:Unicode::UTF8 がガチ爆速すぎる - bayashi.netencode より decode のが差が大きい感じ。encode だけだと、文字列長くなると Encode の方が速いっぽい。
まずは改めて検証してみましょう。
https://gist.github.com/dankogai/5079930確かにその通りになっています。Unicode::UTF8
はEncode
はおろかPerl組み込みのutf8::decode
より高速なのか(文字列をコピーしなければutf8::decodeが最速だけど、破壊的操作なのでベンチマークではコピーをとっている点に留意)。逆になぜencodeではEncodeの方が高速なのか。
答えは、 validation にあります。 decode では入力が正しい UTF-8 になっているかをいずれもチェックしていますが、Unicode::UTF8ではこの部分が最適化されています。その一方、Encodeでは他の文字コードとの共通APIのために余計なオーバーヘッドがあります。特に大きいのは PerlIO とのやりとりで、そのためのメソッド呼び出しが必ず一回以上発生します。
その一方 encode に validation は不要で、必ず成功します。このことは、プラグマではなくモジュールとしてのutf8
を見てもわかります。
$success = utf8::decode($string) Attempts to convert in-place the octet sequence in UTF-X to the corresponding character sequence. That is, it replaces each sequence of characters in the string whose ords represent a valid UTF-X byte sequence, with the corresponding single character. The UTF-8 flag is turned on only if the source string contains multiple-byte UTF-X characters. If $string is invalid as UTF-X, returns false; otherwise returns true.
utf8::encode($string) Converts in-place the character sequence to the corresponding octet sequence in UTF-X. That is, every (possibly wide) character gets replaced with a sequence of one or more characters that represent the individual UTF-X bytes of the character. The UTF8 flag is turned off. Returns nothing.
それでは、実際の利用シーンではどうするのが一番よいのでしょう?
結論を先に書くと、以下のとおりとなるでしょうか。
- UTF-8文字列がソースコード中に全てある場合、
use utf8;
プラグマを指定するだけ。すでにdecodeされた状態になっているので、再デコードは不要。 - UTF-8文字列をファイルから読み込む場合
- 検証が不要なら、ファイルハンドルに
:utf8
を指定binmode STDIN, ':utf8'; # NO VALIDATION APPLIED binmode STDOUT, ':utf8'; while (<>) { # do something to $_ print $_ } close $rfh;
:encoding(utf8)
とは異なる点に留意。この場合は検証も行われます。 - 必要なら、読んだ後
utf8::decode()
を適用open my $rfh, "<", $infile or die "$infile:$_"; open my $rwh, ">:utf8", $outfile or die "$outfile:$_"; # no validation needed while (<$rfh>) { utf8::decode($_) or die "Malformed UTF-8 at line $."; # do something to $_ print {$wfh} $_ } close $rfh; close $wfh;
- 検証が不要なら、ファイルハンドルに
- その他外部から取り込む場合、
utf8::decode()
で検証map { utf8::decode($_) or die "Malformed UTF-8" } @ARGV;
Unicode::UTF8::decode_utf8()
は確かに高速なのですが、Encodeの代わりならとにかく、utf8::decode()
を置き換えるほどではありませんし(Signature後ろのベンチマーク参照)、UTF-8しか絡まないシーンで Encode は今となっては Overkill です。
Dan the Man with too Many Bytes to Transcode
追記:
404 Blog Not Found:#perl - utf8::decode()ではなくEncode::decode_utf8()を使うべき理由見ての通り、utf8::decode()は、不正なUTF-8バイト列に対して何もしません
が、現在のutf8::decode()は少なくとも不正かどうかは返り値で判定できるようになっています(たしか utf-8-strict が加わったあたりから)。本記事の例ではそれを利用して不正なUTF-8を弾いています。例えばUTF-8として不正なバイト列を\x80
のように置き換えたい場合などには Encode::decode_utf8 ないし find_encoding('UTF-8')->decode は依然意味がありますが、「不正ならそこでdie」のような果断でもよければ--おそらく大抵の場合が該当--、utf8::decodeでも充分目的が達成できるというわけです。
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。