Perl LWP::UserAgent 错误处理 UTF-8 响应

Posted

技术标签:

【中文标题】Perl LWP::UserAgent 错误处理 UTF-8 响应【英文标题】:Perl LWP::UserAgent mishandling UTF-8 response 【发布时间】:2011-06-02 02:27:24 【问题描述】:

当我使用 LWP::UserAgent 检索以 UTF-8 编码的内容时,LWP::UserAgent 似乎无法正确处理编码。

这是通过命令chcp 65001 将命令提示符窗口设置为 Unicode 后的输出注意,这最初看起来一切都很好,但我认为这只是外壳重新组装字节并解码 UTF-8,从另一个输出你可以看到 perl 本身没有正确处理宽字符。

C:\>perl getutf8.pl ================================================== ===================== HTTP/1.1 200 正常 连接:关闭 日期:格林威治标准时间 2010 年 12 月 31 日星期五 19:24:04 接受范围:字节 服务器:Apache/2.2.8 (Win32) php/5.2.6 内容长度:75 内容类型:应用程序/xml;字符集=utf-8 最后修改时间:格林威治标准时间 2010 年 12 月 31 日星期五 19:20:18 客户日期:格林威治标准时间 2010 年 12 月 31 日星期五 19:24:04 客户端对端:127.0.0.1:80 客户响应编号:1 布杰约维奇布德瓦尔 ================================================== ===================== 响应内容长度为 33 ....v....1....v....2....v....3....v....4 布杰约维奇布德瓦尔 . . . .诉. . . 1. . . .诉. . . 2. . . .诉. . . 3. . . . 3c6e616d653e427564c49b6a6f7669636bc3bd204275647661723c2f6e616d653e B u d � � j o v i c k � � B u d v a r n a m e >

您可以在上面看到有效负载长度是 31 个字符,但 Perl 认为它是 33 个字符。 为了确认,在十六进制中,我们可以看到 UTF-8 序列 c49b 和 c3bd 被解释为四个单独的字符,而不是两个 Unicode 字符。

这是代码

#!perl 使用严格; 使用警告; 使用 LWP::UserAgent; 我的 $ua = LWP::UserAgent->new(); 我的 $response = $ua->get('http://localhost/Bud.xml'); if (!$response->is_success) die $response->status_line; print '='x70,"\n",$response->as_string(), '='x70,"\n"; 我的 $r = $response->decoded_content((charset => 'UTF-8')); $/ = "\x0d\x0a"; # 似乎是 \x0a 否则! 咀嚼($r); # 删除任何 xml 序言 $r =~ s/^\x0d\x0a//; print "响应内容长度为", length($r), "\n\n"; 打印“....v....1....v....2....v....3....v....4\n”; 打印 $r,"\n"; 打印 ".. . . v . . . . . 1 . . . . . v . . . . 2 . . . . v . . . . 3 . . . . \n"; 打印解包(“H*”,$r),“\n”; 打印连接(“”,拆分(“”,$r)),“\n”;

请注意,Bud.xml 是 UTF-8 编码的,没有 BOM。

如何说服 LWP::UserAgent 做正确的事?

附言最终我想将 Unicode 数据转换为 ASCII 编码,即使这意味着用 一个 问号或其他标记替换每个非 ASCII 字符。


更新 1

我接受了 Ysth 的“升级”答案——因为我知道在可能的情况下这样做是正确的。但是,有一种解决方法可以将数据修复为格式良好的 Perl Unicode 字符串。

$r = decode("utf8", $r);

更新 2

我的数据被馈送到一个非 Perl 应用程序,该应用程序使用 Code Page 437 将数据显示到多个位置的 Putty/Reflection/Teraterm 终端。该应用当前显示如下内容:

Bud├ä┬øjovick├â┬¢ Budvar

我将使用($r = decode("UTF-8", $r)) =~ s/[\x80-\xFFFF]/\xFE/g; 来显示应用:

布德■约维克■布德瓦

摆脱 CP437 将是一项重大工作,因此在中短期内不会发生这种情况。


更新 3

CPAN 有一些有趣的 Unicode 模块,例如:

文本::Unidecode Unicode::Map8 Unicode::地图 Unicode::Escape Unicode::音译

Text::Unidecode 将“Budějovický Budvar”翻译成“Budejovicky Budvar”——在我看来,语音音译的尝试并不特别令人印象深刻,但我不会说捷克语。不过,说英语的人可能更喜欢“Bud■jovick■ Budvar”。

【问题讨论】:

进行完整性检查,您使用的是什么版本的 LWP? @ysth LWP::UserAgent 2.036 版,LWP 5.808 版 您所说的将 21 位 Unicode 数据转换为 7 位 ASCII 数据的最终目标令人沮丧,因为我自己在将大约 100k 文档均质化为 Unicode 时遇到了不小的麻烦。在 11,542 个文档中,10,997 个使用 UTF-8 (95.39%),539 个使用 ISO-8859-1 (4.67%),5 个使用 Shift_JIS (0.04%),仅 1 个使用 Windows-1252 (0.0087%) .您希望集中在 21 位字符上,而不是 7 位字符上!欢迎来到新千年,它的第二个十年即将在不到 5 小时内开始。 单播到带有? 的事物的另一个问题是,在标准文档中非常 特别不建议执行此特定操作.您无法将有效的? 与无效的? 区分开来。 C0 控制代码在这里可能很有用。字符 1A 是 SUBSTITUTION 字符,它在 ASCII 中的工作方式与 U+FFFD REPLACEMENT CHARACTER 在 Unicode 中的工作方式非常相似。 1A 表示“最初打算用作传输控制字符,以指示已收到乱码或无效字符。” U+FFFD 现在为我们履行了这项职责。请不要单播您的数据。 @tchrist:请参阅问题末尾的更新,我认为最后一个稍微好一些。 Putty 忽略了 \x1A,所以我使用了 \xFE (■),因为我觉得给出一些缺失字符的视觉指示会更好。 【参考方案1】:

升级到更新的 libwwwperl。您使用的旧版本仅支持 text/* 内容类型的 decoded_content 的 charset 参数;较新的版本也适用于 application/xml 或任何以 +xml 结尾的内容。

【讨论】:

以上是关于Perl LWP::UserAgent 错误处理 UTF-8 响应的主要内容,如果未能解决你的问题,请参考以下文章

为啥当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not All

未通过 https 使用 LWP::UserAgent get() 获取预期内容

如何使用 LWP::UserAgent 接受自签名证书

在 Perl 中使用 LWP 登录网站

perl爬虫01-花瓣网相册

为啥 LWP::UserAgent 是通过 require LWP::UserAgent 而不是使用 LWP::UserAgent 导入的?