UTF-16 Perl 输入输出

4
我正在编写一个脚本,它以UTF-16编码的文本文件作为输入,并输出一个UTF-16编码的文本文件。
use open "encoding(UTF-16)";

open INPUT, "< input.txt"
   or die "cannot open > input.txt: $!\n";
open(OUTPUT,"> output.txt");

while(<INPUT>) {
   print OUTPUT "$_\n"
}

假设我的程序将input.txt中的所有内容写入output.txt。

在使用“ This is perl 5,version 14,subversion 2(v5.14.2)built for cygwin-thread-multi-64int”的cygwin环境中,这个程序完美地工作。

但是在使用“ This is perl 5,version 12,subversion 3(v5.12.3)built for MSWin32-x64-multi-thread”的Windows环境中,除了第一行之外,output.txt中的每一行都以疯狂的符号为前缀。

例如:

<FIRST LINE OF TEXT>
਀    ㈀  ㄀Ⰰ ㈀Ⰰ 嘀愀 ㌀ 䌀栀椀愀 䐀⸀⸀⸀  儀甀愀渀最 䠀ഊ<SECOND LINE OF TEXT>
...

有人能解释一下为什么在Cygwin上能够运行,但在Windows上不能吗?

编辑:按照建议打印编码层后,在Windows环境中:

unix
crlf
encoding(UTF-16)
utf8
unix
crlf
encoding(UTF-16)
utf8

在Cygwin环境中:
unix
perlio
encoding(UTF-16)
utf8
unix
perlio
encoding(UTF-16)
utf8

唯一的区别在于perlio和crlf层之间。

也许那些“疯狂的符号”是因为你所使用的查看工具无法显示UTF16编码的窗口。 ;) - Brian Roach
我正在使用Notepad++来显示output.txt。如果我使用cygwin运行脚本并生成文件,它可以正常工作,但是如果我使用Windows运行脚本,它也会充满疯狂的符号。 - allenylzhou
尝试将您的Windows Perl升级到5.14或5.16,这将消除这是5.12错误的可能性。可以使用Strawberry PerlActivePerl进行升级。 - Schwern
2个回答

5

我本来想等一下再给你一个详细的回答,但是如果不给你一个快速的答案可能会更好。问题在于和层次顺序错了,这不是你的错。

比如说,假设你使用UTF-16le(因为它更简单,而且很可能是你真正想要的),然后执行print "a\nb\nc\n";,你最终得到的结果将会是:

61 00 0D 0A 00 62 00 0D 0A 00 63 00 0D 0A 00

替代

61 00 0D 00 0A 00 62 00 0D 00 0A 00 63 00 0D 00 0A 00

我认为使用 open 命令或者 binmode 命令并不能得到正确的结果,但是可以通过使用 open 命令来实现。

open(my $fh, '<:raw:encoding(UTF-16):crlf', $qfn)

如果是较旧的版本,您需要附加一个:utf8

它在cygwin上可以工作,因为Windows只添加了crlf层。在那里您会得到:

61 00 0A 00 62 00 0A 00 63 00 0A 00

我并不完全理解这些不同编码层的作用。但是这个方法解决了我的问题:打开我的 $output,">:raw:encoding(UTF-16)", "output.txt"; 添加 :crlf 似乎没有什么区别(这很奇怪,因为你说问题出现在错误的顺序上)。但是添加 :raw 是必要的(否则会出现相同的问题)。 - allenylzhou
有 :crlf 和没有 :crlf 的区别在于所使用的行尾符(CR LF vs LF)。 - ikegami

4
你的编码有错。正确的写法应该是use open ":encoding(UTF-16)",注意冒号。我不知道为什么在Cygwin上可以工作但在Windows上却不能,可能是5.12和5.14的区别。Perl似乎可以弥补这个问题,但也可能是导致你的问题的原因。
如果这样做没有解决问题,请检查编码是否被应用到文件句柄中。
print map { "$_\n" } PerlIO::get_layers(*INPUT);
print map { "$_\n" } PerlIO::get_layers(*OUTPUT);

使用词法文件句柄(即open my $fh, "<", $file)。全局文件句柄是全局的,因此您程序中的其他内容可能会干扰它们。
如果所有检查都通过,并且词法文件句柄正在应用encoding(UTF-16),请让我们知道,我们可以尝试其他方法。
更新:这可能提供了您的答案:“BOMed UTF files are not suitable for streaming models, and they must be slurped as binary files instead.”看起来您必须按二进制方式读取文件并将编码作为字符串。这可能是5.14中修复的错误。
更新2:是的,我可以确认这是在5.14中被修复的错误

正如你建议的那样,我添加了冒号并改用词法文件句柄,但没有任何效果。请查看我问题的编辑以获取打印输出。唯一的区别是在Windows环境中有一个crlf层,在cygwin环境中有一个perlio层。 - allenylzhou
@aylz5073 请查看更新。您可能在5.12中遇到了UTF-16编码错误。 - Schwern
我刚刚尝试了ActivePerl 5.16,但它并没有解决问题。我想要提出的另一个观察结果是,如果我将编码从“:encoding(UTF-16)”更改为“:encoding(UTF-16LE)”,那么output.txt就会变成一种二进制文件,其中充满了NUL标记,而不仅仅是在我的文本行前面添加一些奇怪的符号,如我原始帖子中所示。我想我会尝试您提供的链接中的解决方案,并随时向您更新。 - allenylzhou
@aylz5073,我能想到的唯一方法就是尝试使用草莓 Perl。 - Schwern
与BOM无关。使用UTF-16le,您将得到相同的问题。请参阅我的答案。 - ikegami
感谢你们的帮助,我非常感激。 - allenylzhou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接