6

ここ数時間、Perl プログラムのバグと戦っています。私が何か間違っているのか、インタープリターが間違っているのかはわかりませんが、コードは決定論的である必要がありますが、IMO は非決定論的です。また、古い Debian Lenny (Perl 5.10.0) と Debian Wheezy (Perl 5.14.2) にアップグレードしたばかりのサーバーでも同じ動作を示します。それは、次の Perl コードに要約されます。

#!/usr/bin/perl
use warnings;
use strict;
use utf8;
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";
my $c = "";
open C, ">:utf8", \$c;
print C "š";
close C;
die "Does not happen\n" if utf8::is_utf8($c);
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";

Perl 5 インタープリターを厳密モードで初期化し、警告を有効にして、文字列 (バイト文字列ではなく) と名前付きの標準ストリームを UTF8 でエンコードします (UTF-8 の内部概念ですが、完全な UTF-8 に変更しても違いはありません)。 )。次に、「メモリ内ファイル」(スカラー変数) へのファイル ハンドルを開き、2 バイトの UTF-8 文字を 1 つ出力し、閉じたときに変数を調べます。

スカラー変数の UTF8 ビットが常にオフになります。ただし、バイト文字列 ( を介して文字列に変換されるutf8::decode()) が含まれることもあれば、UTF8 ビットをオンにする必要がある文字列 ( ) が含まれることもありますEncode::_utf8_on()

コードを繰り返し (Bash 経由で 1000 回) 実行するUndecodedDecoded、ほぼ同じ頻度で出力されます。「ファイル」に書き込む文字列を変更すると、たとえば最後に改行を追加すると、Undecoded消えます。成功し、ループ内の同じ元の文字列に対して試行するutf8::decodeと、インタープリターの同じインスタンスで成功し続けます。ただし、失敗すると、失敗し続けます。

観察された行動の説明は何ですか? 文字列と一緒にスカラー変数へのファイル ハンドルを使用するにはどうすればよいですか?

バッシュ遊び場:

for i in {1..1000}; do perl -we 'use strict; use utf8; binmode STDOUT, ":utf8"; binmode STDERR, ":utf8"; my $c = ""; open C, ">:utf8", \$c; print C "š"; close C; die "Does not happen\n" if utf8::is_utf8($c); print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";'; done | grep Undecoded | wc -l

参考までに、確実に確認するために、ペダンティックなエラー処理を備えたバージョンも作成しましたが、結果は同じでした。

#!/usr/bin/perl
use warnings;
use strict;
use utf8;
binmode STDOUT, ":utf8" or die "Cannot binmode STDOUT\n";
binmode STDERR, ":utf8" or die "Cannot binmode STDERR\n";
my $c = "";
open C, ">:utf8", \$c or die "Cannot open: $!\n";
print C "š" or die "Cannot print: $!\n";
close C or die "Cannot close: $!\n";
die "Does not happen\n" if utf8::is_utf8($c);
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";
4

1 に答える 1

4

詳細に調べると、その内容や内部$cとは何の関係もないことがわかり、 の結果は、それが何をしたか、何をしなかったかを正確に表しています。$cdecode

$ for i in {1..2}; do
     perl -MDevel::Peek -we'
        use strict; use utf8;
        binmode STDOUT, ":utf8";
        binmode STDERR, ":utf8";
        my $c = "";
        open C, ">:utf8", \$c;
        print C "š";
        close C;
        die "Does not happen\n" if utf8::is_utf8($c);
        Dump($c);
        print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";
        Dump($c)
     '
     echo
  done

SV = PV(0x17c8470) at 0x17de990
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x17d7a40 "\305\241"
  CUR = 2
  LEN = 16
Decoded
SV = PV(0x17c8470) at 0x17de990
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x17d7a40 "\305\241" [UTF8 "\x{161}"]
  CUR = 2
  LEN = 16

SV = PV(0x2d0fee0) at 0x2d26400
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x2d1f4b0 "\305\241"
  CUR = 2
  LEN = 16
Undecoded
SV = PV(0x2d0fee0) at 0x2d26400
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x2d1f4b0 "\305\241"
  CUR = 2
  LEN = 16

これは のバグでしたがutf8::decode、5.16.3 以前 (5.14.2 にも存在していたので、おそらく 5.16.0) で修正されました。

decode_utf8代わりにEncode を使用するのが適切な回避策です。

于 2014-08-09T05:07:32.440 に答える