【问题标题】:LWP::Simple::get changes encodingLWP::Simple::get 更改编码
【发布时间】:2016-11-05 00:33:52
【问题描述】:

我想我使用 LWP::Simple::get 不正确,但我不知道如何纠正它。我的第一次尝试很简单

perl -e 'use LWP::Simple; print get("http://localhost/wtf.txt");'

,但这不起作用。 wtf.txt 包含单个 UTF-8 编码字符 u+00f6(即 ö)。使用wgetxxd 我确保HTTP 服务器发送正确的标题行Content-Type: text/plain; charset=utf-8 并且内容符合预期。但上面的 perl 代码却将u+00f6 作为 ISO-8859-1-encoded 返回。

我认为这是一个简单的编码问题,但通过简单的修复,我发现它并不像我希望的那样简单。我使用单个 UTF-8 编码字符 u+30e4(即 )创建了第二个文件 wtf2.txt,并使用以下 perl 代码获取了这两个文件:

#!/usr/bin/perl
use LWP::Simple;
$wtf=get("http://localhost/$ARGV[0]");
$wtf2=pack("H*",unpack("H*",$wtf));
print $wtf;
print "\n";
print $wtf2;
print "\n$wtf\n$wtf2\n";
print (unpack("H*",$wtf)."\n");

在获取 wtf.txt 时,此代码以 ISO-8859-1 编码的形式写入 4 次 u+00f6,然后是 f6(其 ISO-8859-1 编码的十六进制形式)。到这里,一切都和以前一样。但是在获取wtf2.txt 时,此代码以UTF-8 编码形式写入u+30e4,然后是ISO-8859-1 中的u+00e4(即ä)、UTF-8 中的u+30e4、@987654342 @ in UTF-8, e4 (ISO-8859-1 of u+00e4 in hex)。

鉴于 u+30e4u+00e4 彼此无关,除了后者是前者的位掩码/截断版本,我希望不仅重新编码发生在 LWP::Simple 内部,而且一些截断。我倾向于向 LWP::Simple 提交错误报告,但我仍然希望得到一个简单的修复和/或解释。

顺便说一句,如果我将第二行和第三行替换为 $wtf=<>; 并简单地从 stdin 读取文件而不是通过 LWP::Simple::get 获取它们,则不会出现上述问题。

我在 Debian 7 上使用 perl 5.14.2 和 libwww 6.04 对此进行了测试。

【问题讨论】:

标签: perl libwww-perl


【解决方案1】:

这是您代码中的错误。

LWP::Simple::get 不返回原始字节(以某种编码方式),它返回解码后的文本(即 Unicode)。 (这是有道理的,因为如果它返回字节,您将不知道如何解码它们,因为get 不会告诉您编码。)

所以get("http://localhost/wtf.txt") 返回一个包含代码点 U+00f6 的字符串。 print 然后将一些字节写入 STDOUT。这些字节是什么?这取决于当前在文件句柄上设置的编码层。默认情况下,这是 Latin-1 和 UTF-8 的奇怪组合(它甚至可能取决于字符串的内部编码)。

如果您想获得 UTF-8 输出,请先执行binmode STDOUT, ":encoding(UTF-8)";。这样可以确保写入 STDOUT 的所有文本都被编码为 UTF-8。

另一方面,如果您想忽略编码并只写入从 Web 服务器接收到的字节,那么 LWP::Simple 是错误的选择。请改用LWP::UserAgent 并致电$response->content。 (LWP::Simple::get 在内部使用 $response->decoded_content。)

第二个示例中的截断可能是由于 pack/unpack,这对 Unicode 字符串没有意义(它们适用于字节字符串,即所有代码点

【讨论】:

  • 谢谢。 binmode STDOUTLWP::UserAgent 都可以工作。 pack/unpack 是查看 perl 数据的十六进制版本的推荐方法。有没有更好的选择,让我对 perl 在其变量中存储的内容有一个不变的十六进制/八进制/十进制视图?如果我有这个,我可以自己调试它,而不必用它来打扰 stackoverflow。
  • @user2845840 如果你想看看 perl 认为它的字符串中有什么,使用printf "%vd\n", $str(十进制)或printf "%vx\n", $str(十六进制)。输出将采用“点分十进制”(或十六进制)形式,每个代码点有一个数字(这也告诉您 perl 认为字符串的长度是(点数 + 1))。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 1970-01-01
  • 2021-06-08
  • 2011-09-22
  • 2016-09-05
  • 1970-01-01
  • 2011-06-30
相关资源
最近更新 更多