如何在Perl中读取无缓冲的UTF-8

时间:2013-06-29 13:45:52

标签: perl utf-8 buffering

我正在尝试以无缓冲的方式读取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的另一种脚本语言?

3 个答案:

答案 0 :(得分: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 ...
   }
}

答案 1 :(得分:1)

在utf-8模式下,read会对部分字符重试。这种破坏你特别使用read-on-:unix。我想这是“不要这样做”的情况。

在这种特殊情况下,getc可能有用。这将读取必要的最低限度。在其他情况下,之后的解码可能是更好的选择。

答案 2 :(得分: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;
    }
}