如何在Perl中将输入文件转换为UTF-8编码?

7

我已经知道如何逐行将非UTF-8编码的文件内容转换为UTF-8编码,可以使用以下类似代码:

# outfile.txt is in GB-2312 encode    
open my $filter,"<",'c:/outfile.txt'; 

while(<$filter>){
#convert each line of outfile.txt to UTF-8 encoding   
    $_ = Encode::decode("gb2312", $_); 
...}

但是我认为Perl可以直接将整个输入文件编码为UTF-8格式,所以我尝试了以下代码:

#outfile.txt is in GB-2312 encode
open my $filter,"<:utf8",'c:/outfile.txt'; 

(Perl会提示类似于"utf8 "\xD4"无法映射到Unicode")

以及

open my $filter,"<",'c:/outfile.txt'; 
$filter = Encode::decode("gb2312", $filter); 

(Perl提示“readline() on unopened filehandle!”)

它们无法工作。但是否有一种直接将输入文件转换为UTF-8编码的方法?

更新:

看起来事情并不像我想象的那么简单。现在,我可以用迂回的方式将输入文件转换为UTF-8代码。我先打开输入文件,然后将其内容编码为UTF-8,输出到一个新文件中,然后打开新文件进行进一步处理。这是代码:

open my $filter,'<:encoding(gb2312)','c:/outfile.txt'; 
open my $filter_new, '+>:utf8', 'c:/outfile_new.txt'; 
print $filter_new $_ while <$filter>; 
while (<$filter_new>){
...
} 

但是这太麻烦了,甚至比逐行编码$filter的内容还要麻烦。


3
在问题中提到警告信息时,请在问题中包含该警告信息。 :) - brian d foy
1
最好使用确切的警告信息 :) 因此,在收到该警告后,您需要检查打开操作的结果(无论如何,您都应该这样做)。 - brian d foy
工作太多了吗?那看起来非常简单,只需要几行代码就可以完成。将其包装在子程序中,然后你就完成了。不过我不确定你为什么要使用'+>'打开一个文件。 - brian d foy
我使用"+>"这个符号,这样我就可以先写文件再打开文件。以下是这三行代码的作用: open my $filter_new, '+>:utf8', 'c:/outfile_new.txt'; print $filter_new $_ while <$filter>; while (<$filter_new>){ 或者说我做了不必要的事情吗? - Mike
1
好的,如果你想阅读它,你需要寻找开头。 - brian d foy
@brian,感谢指点!是的,有些地方似乎不对。我猜我需要寻找开头。 - Mike
2个回答

5
我认为我误解了你的问题。我认为你想做的是在非UTF-8编码下读取文件,然后在程序中将数据转换为UTF-8进行操作。这个要简单得多。在使用正确的编码读取数据后,Perl会在内部以UTF-8表示它。所以,只需按照需要进行操作即可。
当你将其写回时,可以使用任何编码来保存它。但是,你不必将其放回文件中才能使用它。

旧答案

Perl I/O层只读取已经正确编码的数据。它不会为你转换编码。通过告诉open使用utf8,你告诉它已经是utf8了。

你必须像你展示的那样使用Encode模块(除非你想自己编写I/O层)。你可以将字节转换为UTF-8,或者如果你知道编码,可以从一种编码转换为另一种编码。由于看起来你已经知道编码,你可能需要from_to()函数。

如果你刚开始学习Perl和Unicode,请先阅读Juerd的Perl Unicode建议


@Brian,感谢您的指导。我原以为打开输入文件时应该有一种简单的方法可以直接将其转换为UTF-8编码。但现在看来事情并不那么简单。我想我可以先打开输入文件,然后将内容编码为UTF-8,再将其输出到另一个以UTF-8编码的文件中,最后再打开那个文件。代码如下: open my $filter,'<:encoding(gb2312)','c:/outfile.txt'; open my $filter_new, '+>:utf8', 'c:/f2.txt'; print $filter_new $_ while <$filter>; while (<$filter_new>){...} 但这太麻烦了。while(<$fh_out>){ - Mike
你对太多工作的想法是错误的。试着手动完成它,然后回来告诉我们Perl让它变得多么容易。现在的孩子们真不知道自己有多幸福 :) - brian d foy
我非常确定(在问题的原始部分中更清楚,我认为)他想要的只是将数据从文件中转换,而不是转换文件本身。但是,是的,要做后者,仅仅读取是不够的。 - ysth
@ysth,我猜我之前表达问题有误。实际上,我想要的是将输入文件转换为 UTF-8,然后执行 readline 操作。我已经知道如何在使用 while 循环进行 readline 操作时转换输入文件的数据了。不过还是谢谢你的帮助。 - Mike
@brian,嗯,看待我的问题的一种方式是:“有没有一些更好的方法来读取非UTF-8编码的文件,然后将数据作为UTF-8处理?”所谓“更好的方法”,指的是不是我已经学过的逐行转换方法。 - Mike
显示剩余2条评论

4
:encoding 层将返回适合 Perl 使用的 UTF-8 编码。也就是说,即使字符由多个字节组成,Perl 也会将每个字符识别为一个字符。根据您接下来要处理数据的方式,这可能已经足够了。
但是,如果您要对数据进行某些操作,其中 Perl 将尝试将其从 utf8 转换为其他编码,则需要告诉 Perl 不要这样做(例如,执行 binmode(STDOUT, ":utf8") 告诉 Perl 输出到 stdout 的内容应该是 utf8),或者您需要让 Perl 将您的 utf8 视为二进制数据(分别解释每个字节,并不知道 utf8 字符)。
要实现这一点,您只需要在打开时应用一个额外的层:
open my $foo, "<:encoding(gb2312):bytes", ...;

请注意,以下输出结果相同:

perl -we'open my $foo, "<:encoding(gb2312):bytes", "foo"; $bar = <$foo>; print $bar'
perl -CO -we'open my $foo, "<:encoding(gb2312)", "foo"; $bar = <$foo>; print $bar'

但是在某些情况下,Perl 知道读取的数据是 utf8 编码(所以 length($bar) 将报告 utf8 字符的数量),必须显式地告知 (-CO) STDOUT 接受 utf8。而在另一种情况下,Perl 不会对数据作出任何假设(因此 length($bar) 将报告字节的数量),并仅将其原样打印出来。

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