【问题标题】:Perl recognizing utf8 as Unicode after 4096 bytesPerl 在 4096 字节后将 utf8 识别为 Unicode
【发布时间】:2013-09-08 00:44:57
【问题描述】:

我有一个 Perl/CGI 应用程序,我在其中接收一个 utf8 txt 文件并处理其内容。

由于某种原因(我认为 Perl 将文件划分为 4096 字节的缓冲区,并且只有第一个具有字节顺序标记)Perl 将文件的内容解释为 4096 字节之后的 Unicode。

如果我在文件中间散布一些短划线(“-”)(每个 4k 块至少一个),程序会将其识别为 utf8,可能是因为 Unicode 没有短划线。

我从 html 页面接收 txt 并将其发送到这样的标量变量:

while(my $l = <$fh>){
    $text .= $l;
}

我尝试通过用短划线连接文件的每一行来强制使用 utf8:

while(my $l = <$fh>){
    $text .= "–".$l;
}

但我收到此错误:

Wide character in print at (eval 12) line 94.

有人有小费吗? 有 谢谢!

【问题讨论】:

标签: perl unicode utf-8 buffer byte-order-mark


【解决方案1】:

Perl 可以对 Unicode 代码点进行操作,但所有 I/O 都是以字节完成的。当您将具有高代码点的字符串打印到普通文件句柄时,您会收到“打印中的宽字符”警告。

您应该解码所有输入数据,并编码所有输出。最好的方法是使用 PerlIO 层。您可以使用binmode 添加图层。例如:

use utf8; # This source file is encoded in UTF-8.
          # Else, the literal "–" would be seen as multiple bytes, not one single character.

binmode STDOUT, ":uft8"; # encode all strings (that get printed to STDOUT)
                         # to the binary UTF-8 representation
print "–\n"; # EN DASH – works.

打开文件时,可以在打开模式下添加 PerlIO 层,例如

open my $fh "<:utf8", $filename or die ...;

这会将二进制输入透明地转换为代码点。

不要将包含二进制 UTF-8 的字节字符串与正确解码的字符串连接 - 结果很可能是无效数据。当然,解码所有输入时不会出现此类问题。

Perl 缓冲输入的方式不应该影响你的程序;很可能你误诊了。 Perl 不通过 BOM 对输入文件进行编码检测。

在 Web 编程环境中,将输出编码为 UTF-8 是一个不错的选择,但请确保在响应标头中设置 charset 属性:

Content-Type: text/html; charset=UTF-8

HTML 文档应使用&lt;meta charset="UTF-8"&gt; 重申这一点。

【讨论】:

  • 嗨,阿蒙,感谢您的回复。 “将具有高代码点的字符串打印到普通文件句柄”是什么意思?我已经再次收到“印刷中的宽字符”警告,但从未真正理解这一点。每次从磁盘打开文件时,我都会使用: open FH, "<:encoding cgi>
  • @gvieira 这很好,但是您在打印出来时还需要对数据进行编码。这可以通过binmode STDOUT, ":utf8"(或您想要的任何文件句柄和编码)来完成。然后该警告应该消失。高代码点是不是 Latin-1 (IIRC) 中的任何字符。
  • @gvieira 该回复是在您编辑之前写的。您需要在输入和输出文件句柄上添加:utf8 层(或等效层)。在这两种情况下,都可以使用binmode 添加图层,如图所示。
  • 我正在做一个测试:binmode $fh, ":utf8"; open (AUXT, "&gt;"."auxtext.txt"); binmode AUXT, ":utf8"; while (&lt;$fh&gt;){ print AUXT $_; } close AUXT; 我仍然得到宽字符只是因为上传的文件有一个'ç'。怎么可能发生?编辑:error.log 说宽字符错误在print AUXT $_;
  • @gvieira 您是否偶然使用了CGI 模块中的upload 函数?在这种情况下,请在获得$fh = $fh-&gt;handle 后立即尝试。您能否在使用binmode 指定它们之后列出所有应用的 PerlIO 层?您可以像say for PerlIO::get_layers(AUXT); 等一样这样做。
【解决方案2】:

试试:

use Encode qw(encode);

$text = join '', <$fh>;

$text = encode("utf8", $text);

【讨论】:

    猜你喜欢
    • 2016-12-29
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    相关资源
    最近更新 更多