1

Perl で UTF-8 入力をバッファーなしで読み取ろうとしています (つまり、データが利用可能になるとすぐに返されます)。

die if !binmode STDIN, ':unix:utf8';
my $i;
my $buf;
while ($i = read(STDIN, $buf, 8192)) {
  print "$i\n";
}

ただし、入力に UTF-8 文字分割が含まれている場合は機能しません。

$ perl -e '$|=1;print"\xc3";sleep 1;print"\xa1";sleep 1;print"AB"' | perl t.pl

これは 1 を出力し、次に 2 を出力するはずですが、3 を出力するため、最初の文字が使用可能になった後でもバッファリングによって保留されます。

Perl でこれに対する簡単な解決策はありますか? それとも、Unix 用の別のスクリプト言語でしょうか?

4

3 に答える 3

4

readまず、 からに変更する必要がありますsysreadread要求された文字数になるまで読み取りますがsysread、データが利用可能になるとすぐに戻ります。

しかし、到着するとすぐにデータを返すということは、最後に不完全な UTF-8 文字がある可能性があることを意味するため、完全に受信した文字のみをデコードし、残りをバッファリングする必要があります。

sub decode_utf8_partial {
   my $s = decode('UTF-8', $_[0], Encode::FB_QUIET);
   return undef
      if !length($s) && $_[0] =~ /
         ^
         (?: [\x80-\xBF]
         |   [\xC0-\xDF].
         |   [\xE0-\xEF]..
         |   [\xF0-\xF7]...
         |   [\xF8-\xFF]
         )
      /xs;

    return $s;
}

binmode($fh);

my $buf;
while (1) {
   my $rv = sysread($fh, $buf, 64*1024, length($buf));
   die $! if !defined($rv);
   last if !$rv;

   while (1) {
      # Leaves undecoded part in $buf    
      my $s = decode_utf8_partial($buf);
      die "Bad UTF-8" if !defined($s);
      last if !length($s);

      ... do something with $s ...
   }
}
于 2013-06-29T16:44:58.880 に答える
1

utf-8 モードでは、read は部分文字で再試行します。ただし、そのようなものは read-on-:unix の特定の使用を台無しにします。これは「やってはいけないこと」のケースだと思います。

この特定のケースでは、getc役に立つかもしれません。それは必要最小限を読み取ります。他の状況では、後でデコードする方が適切なオプションになる場合があります。

于 2013-06-29T14:13:48.110 に答える
0

これは機能しているように見えますが、ほとんどの場合、スリープ (おそらく Time::HiRes::sleep) をスローするか、ループに選択する必要があります。

die if !binmode STDIN, ':unix:utf8';
use IO::Handle;
die unless STDIN->blocking(0);
my $i;
my $buf;
while (1) {
    $i = read(STDIN, $buf, 8192);
    if ($i) {
        print "$i\n";
    }
    elsif (defined $i) {
        last;
    }
}
于 2013-06-30T18:03:12.990 に答える