tokuhirom's Blog

Perl5 においてファイルからの読取る場合における Malformed UTF-8 問題

Perl::Critic が最近 PerlIO layer に :utf8 つかうと怒ってくるけど、べらんめえしったことか! とおもっていたら、サイトが表示できなくなった(このような問題を hasegawayosuke 現象と我々はよんでいる)ので、ちゃんと調べた。
https://metacpan.org/module/Perl::Critic::Policy::InputOutput::RequireEncodingWithUTF8Layer

以下のようなスクリプトを実行する。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use 5.010000;
use autodie ':all';
use Devel::Peek;

{
    open my $ofh, '>', 'foo.txt';
    print $ofh "\xC0\xAF";
}

for my $layer ('<:utf8', '<:encoding(utf8)', '<:encoding(utf-8)') {
    print "---- $layer\n";
    open my $fh, $layer, 'foo.txt';
    my $src = do { local $/; <$fh> };
    print "SRC: '$src'\n";
    Dump($src);
    print "\n\n-------\n";
}

以下のような結果をえる。

---- <:utf8
utf8 "\xC0" does not map to Unicode at charset.pl line 17, <$_[...]> chunk 1.
SRC: '/'
SV = PV(0x84ff0d0) at 0x8764768
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x8754358 "\300\257"\0Malformed UTF-8 character (2 bytes, need 1, after start byte 0xc0) in subroutine entry at charset.pl line 19, <$_[...]> line 1.
 [UTF8 "\x{0}"]
  CUR = 2
  LEN = 12


-------
---- <:encoding(utf8)
utf8 "\xC0" does not map to Unicode at charset.pl line 17.
utf8 "\xAF" does not map to Unicode at charset.pl line 17.
SRC: '\xC0\xAF'
SV = PV(0x84ff0d0) at 0x8764768
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x8754358 "\\xC0\\xAF"\0 [UTF8 "\\xC0\\xAF"]
  CUR = 8
  LEN = 12


-------
---- <:encoding(utf-8)
utf8 "\xC0" does not map to Unicode at charset.pl line 17.
utf8 "\xAF" does not map to Unicode at charset.pl line 17.
SRC: '\xC0\xAF'
SV = PV(0x84ff0d0) at 0x8764768
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x8754358 "\\xC0\\xAF"\0 [UTF8 "\\xC0\\xAF"]
  CUR = 8
  LEN = 12


-------

ここで、:encoding(utf8) は utf-8 のことだが :encoding(utf-8) は utf-8-strict をつかうので、よりベターである。

【考察】
":encoding(utf-8)" を素直につかえばいいが、無駄にながいのでどうにかしてほしい。また、Perl::Critic のアドバイスは素直にきくことによって人生をおだやかにすごすことができる。特に自称中級者にかぎってアドバイスを無視する傾向があるので気をつけていきたい。

【参考文献】

【追記】


nihen【:encoding(utf8) は utf-8 のことだが】は間違い。もともとutf8はliberal http://search.cpan.org/~dankogai/Encode-2.44/Encode.pm#UTF-8_vs._utf8_vs._UTF82011/11/02

だそうです!