camel

駄目です。

[を] Perl の utf8 まわりのおまじない
最近良く使うおまじない、というかイディオム。
utf8::decode($text) unless utf8::is_utf8($text);

こういう場合は、Encode::decode_utf8()でないと。

以下をごらんください。

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


for my $bytes ( "\x2F", "\xC0\xAF", "\xE0\x80\xAF", "\xF0\x80\x80\xAF" ) {
    my $utf8 = $bytes;
    utf8::decode($utf8) unless utf8::is_utf8($utf8);
    Dump($utf8);
}

から持ってきた例題ですが、見ての通り、utf8::decode()は、不正なUTF-8バイト列に対して何もしません。

今度はEncode::decode_utf8()を見てみましょう。

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

for my $bytes ( "\x2F", "\xC0\xAF", "\xE0\x80\xAF", "\xF0\x80\x80\xAF" ) {
    my $utf8 = decode_utf8 $bytes;
    Dump($utf8);
 }

今度は不正な文字は、全て\x{fffd}、REPLACEMENT CHARACTERに置き換えられています。

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

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

{
    use bytes;
    my $bytes = '小飼弾';
    Dump($bytes);
    my $utf8 = decode_utf8($bytes);
    Dump($utf8);
}
{
    use utf8;
    my $bytes = '小飼弾';
    Dump($bytes);
    my $utf8 = decode_utf8($bytes);
    Dump($utf8);
}

utf8.pmのPODにもこうあります。

perldoc utf8
$success = utf8::decode($string)

Attempts to convert in-place the octet sequence in UTF-X to the corresponding character sequence. 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.

Note that this function does not handle arbitrary encodings. Therefore Encode is recommended for the general purposes; see also Encode.

utf8::decode()は、定数文字列、すなわちソース内の文字列に限って利用すべきでしょう。外部入力はEncodeに振りましょう。

Dan the Encode Maintainer