このブログはURLが変更になりました

新しいブログはこちら→ https://matsuu.hatenablog.com/

Encode::decode_utf8()であってもis_utf8()を使うべき理由

404 Blog Not Found:#perl - utf8::decode()ではなくEncode::decode_utf8()を使うべき理由

Validationの観点だけではなく、簡潔性の観点からも、Encode::decode_utf8()はおすすめです。すでに UTF-8 flag がついた文字列はそのままコピーするだけなので、条件分岐も不要です。

これは厳密にはこうなる。

Validationの観点だけではなく、簡潔性の観点からも、Encode::decode_utf8()はおすすめです。すでに UTF-8 flag がついた文字列はEncode-2.13以降であればそのままコピーするだけなので、条件分岐も不要です。

Encode-2.12ではそのままコピーしてない。そのままコピーするのは2.13以降での実装。

--- Encode-2.12/Encode.pm       2005-09-08 23:17:34.000000000 +0900
+++ Encode-2.13/Encode.pm       2006-01-16 00:06:45.000000000 +0900
@@ -200,6 +199,7 @@
 sub decode_utf8($;$)
 {
     my ($str, $check) = @_;
+    return $str if is_utf8($str);
     if ($check){
        return decode("utf8", $str, $check);
     }else{

よって、Encode-2.12環境だと、

#!/usr/bin/perl
use strict;
use warnings;
use Encode;
use Devel::Peek;

{
    use utf8;
    my $bytes = '松鵜';
    Dump($bytes);
    my $utf8 = decode_utf8($bytes);
    Dump($utf8);
}

これはエラーになる。

SV = PV(0x7120f0) at 0x72ba70
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,POK,pPOK,UTF8)
  PV = 0x721460 "\346\235\276\351\265\234"\0 [UTF8 "\x{677e}\x{9d5c}"]
  CUR = 6
  LEN = 8
Cannot decode string with wide characters at /usr/lib64/perl5/5.8.8/x86_64-linux/Encode.pm line 166.

よって、例えばこうするのが無難。

#!/usr/bin/perl
use strict;
use warnings;
use Encode;
use Devel::Peek;

{
    use utf8;
    my $bytes = '松鵜';
    Dump($bytes);
    my $utf8 = Encode::is_utf8($bytes) ? $bytes : decode_utf8($bytes);
    Dump($utf8);
}

utf8:is_utf8()ではなくEncode::is_utf8()を使うのが多分スマート。

なんでまだEncode-2.12を使ってるんだ!

と怒られそうだが、開発の現場ではEncode-2.12を使ってる可能性が高い。理由は次のとおり。

  • Perl 5.8.8に含まれるEncodeのバージョンは2.12
  • RHEL5(CentOS 5)のperlのバージョンは5.8.8
  • 日本におけるRHEL5(CentOS 5)の利用率は結構高い。

だったらバージョンを上げればいいじゃない

いやいや、上げられない環境ってのもありまして。

  • バージョンを上げる権限がない
  • 勝手にPerlモジュールをインストールするとサポート対象外
  • perlRPMパッケージで提供されるEncodeとの整合性で云々*1
  • pure perlなモジュールしかインストールできない*2
    • でも性能劣化は避けたい

とかとか。あるよねー。え?ない?俺だけ?

各OSで提供されているPerlのバージョン

ついでなので、他のOSもざっと調べてみた。

OS名 Perlのバージョン 備考
Debian stable(lenny) 5.10.0 oldstable(etch)は5.8.8
Fedora 11 5.10.0 Fedora 8は5.8.8
Gentoo 5.8.8 stableもunstableも5.8.8
openSUSE stable(11.1) 5.10.0 10.3は5.8.8
RHEL5 5.8.8 CentOS5も同様
Ubuntu 9.04 5.10.0 8.04は5.8.8
Vine Linux 5 5.10.0 Vine-4.2は5.8.6
FreeBSD 7.2 5.10.0 7.1は5.8.8
Mac OS X(Snow Leopard) 5.10.0 Leopardは5.8.8

RHEL5Gentoo以外は5.10.0だが、他もつい最近まで5.8.8だった。残念ながらEncode-2.12はまだまだ現役。

Gentooがいまだに5.8.8なのは、SpamAssassinなど5.10.0と組み合わせると行儀が悪いクソモジュールがあるためのようで。いやー大変ですなぁ。

*1:実際は被らないのだが

*2:レンタルサーバ環境など