使用来自基于行的迭代器的CRLF行结尾解码UTF-16

时间:2014-07-08 12:01:34

标签: perl unicode

所以,我有一个返回行的迭代器对象,后来解码,因为迭代器不知道编码。一切正常,直到我尝试添加UTF-16支持。

这里大概是我的代码:

use Encode qw(decode);
my @lines;
my $buffer = '';
while($buffer .= $iter->next){
    push @lines, decode("UTF-16LE", $buffer, Encode::FB_QUIET);
}

数据是带有CRLF线路结尾的小端UTF-16。这是通过od -a抽取的第一行:

0000000   ff  fe   e nul   m nul   a nul   i nul   l nul  ht nul   l nul
0000020    a nul   n nul   g nul   u nul   a nul   g nul   e nul  ht nul
0000040    e nul   x nul   a nul   m nul   p nul   l nul   e nul   _ nul
0000060    t nul   e nul   x nul   t nul  cr nul  nl nul

我遇到的问题是我最终将线路结束分开。第一行以CR结尾,第二行以LF开头,依此类推。

知道这里发生了什么以及如何绕过它?

编辑:

仔细观察,发生了什么是基于行的迭代器返回所有内容并包括LF,但(重要的是)不是后面的空字节。 decode()被设置为仅从缓冲区中读取它可以包含的字符,因此将下一个LF放在缓冲区中。下一次迭代到来并将所有内容添加到下一个LF,其中包括null。所以你最终在一行的末尾有一个CR,在下一行的开头有一个LF。

我不确定该如何处理,除了在顶部有另一个缓冲区,只读取decode()输出的完整行。

有什么建议吗?

2 个答案:

答案 0 :(得分:3)

当您尝试使用期望ASCII的代码读取UTF-16时会发生这种情况。使用ISO 8859-1和UTF-8等ASCII兼容编码,您可以使用期望纯ASCII的代码(只要它是8位清除)读取它们,然后再解码它们。这只适用于UTF-16。 UTF-16LE更糟糕,因为LF表示为0A00,但类似的问题也可能与big-endian一起出现。

当您的文件包含U+010A时会发生什么?还是U+020A?还是U+0A01?或者......

你将不得不教你的迭代器有关编码,或者在迭代器下面插入一层进行解码,或编写一个迭代器,从另一个迭代器中读取“行”并将它们组装成正确解码的实际行。

答案 1 :(得分:2)

看起来你正在使用Iterator::File?我建议你放弃它正是因为这样的问题,因为它没有提供超出标准while (<>) { ... }接口的任何东西。

使用

打开文件
open my $fh, '<:encoding(UTF-16LE)', 'myfile.txt' or die $!;

然后用

阅读
my @lines;
while (my $buffer = <$fh>) {
  push @lines, $buffer;
}

或只是

my @lines = <$fh>;

但您是否考虑过逐行处理文件?您很少需要同时在内存中获取所有数据。