我正在尝试以无缓冲的方式读取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的另一种脚本语言?
答案 0 :(得分:4)
首先,您需要从read
更改为sysread
。 read
会一直读取,直到它有所请求的字符数,而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;
}
}