【问题标题】:How to force ISO-8859-1 encoding for form data using LWP::UserAgent?如何使用 LWP::UserAgent 强制对表单数据进行 ISO-8859-1 编码?
【发布时间】:2011-06-24 22:53:09
【问题描述】:

似乎 LWP::UserAgent 总是将表单数据编码为 UTF-8,即使明确地将其编码为 ISO-8859-1,如下所示:

use Encode;
use LWP::UserAgent;
use utf8;

my $ua = LWP::UserAgent->new;
$ua->post('http://localhost:8080/', {
    text => encode("iso-8859-1", 'è'),
});

请求内容是text=%C3%A8。我怎样才能将è 编码为%E8

【问题讨论】:

  • 你是如何确定请求内容的?网络嗅探器肯定会说text=%E8i.stack.imgur.com/rM3xS.png
  • 有趣。我在端口 8080 上运行nc,我得到text=%C3%A8。规格:MacOS X 10.6,perl v5.10.0,libwww-perl/5.837。

标签: perl lwp-useragent percent-encoding


【解决方案1】:

呵呵。 :-) 这与最近十几个 Perl 版本中对 Unicode 的支持不断增长以及 URI module 使用的正则表达式功能 \C(更准确地说,URI::Escape)有关。阅读this thread on perl-unicode from 2010 (Don't use the \C escape in regexes - Why not?)了解背景。

为什么是URI 模块?因为是HTTP::Request::Common用来做表单和URL编码的。

同时,我写了一个脚本来提醒自己这个问题是多么棘手,尤其是URI 模块是如此常用:

use 5.010;
use utf8;
# Perl and URI.pm might behave differently when you encode your script in
# Latin1 and drop the utf8 pragma.
use Encode;
use URI;
use Test::More;
use constant C3A8 => 'text=%C3%A8';
use constant   E8 => 'text=%E8';
diag "Perl $^V";
diag "URI.pm $URI::VERSION";
my $chars = 'è';
my $octets = encode 'iso-8859-1', $chars;
my $uri = URI->new('http:');

$uri->query_form( text => $chars );
is $uri->query, C3A8, C3A8;

my @exp;
given ( "$^V $URI::VERSION" ) {
        when ( 'v5.12.3 1.56' ) { @exp = (   E8, C3A8 ) }
        when ( 'v5.10.1 1.54' ) { @exp = ( C3A8, C3A8 ) }
        when ( 'v5.10.1 1.58' ) { @exp = ( C3A8, C3A8 ) }
        default                 { die 'not tested :-)' }
}

$uri->query_form( text => $octets );
is $uri->query, $exp[0], $exp[0];

utf8::upgrade $octets;
$uri->query_form( text => $octets );
is $uri->query, $exp[1], $exp[1];

done_testing;

所以我得到的(在 Windows 和 Cygwin 上)是:

C:\Windows\system32 :: perl \Opt\Cygwin\tmp\uri.pl
# Perl v5.12.3
# URI.pm 1.56
ok 1 - text=%C3%A8
ok 2 - text=%E8
ok 3 - text=%C3%A8
1..3

还有:

MiLu@Dago: ~/comp > perl /tmp/uri.pl
# Perl v5.10.1
# URI.pm 1.54
ok 1 - text=%C3%A8
ok 2 - text=%C3%A8
ok 3 - text=%C3%A8
1..3

更新

您可以手工制作请求正文:

use utf8;
use Encode;
use LWP::UserAgent;
my $chars = 'ölè';
my $octets = encode( 'iso-8859-1', $chars );
my $body = 'text=' .
        join '',
        map { $o = ord $_; $o < 128 ? $_ : sprintf '%%%X', $o }
        split //, $octets;
my $uri = 'http://localhost:8080/';
my $req = HTTP::Request->new( POST => $uri, [], $body );
print $req->as_string;
my $ua = LWP::UserAgent->new;
my $rsp = $ua->request( $req );
print $rsp->as_string;

【讨论】:

  • 很好,卢米。那是丑丑丑陋的东西。将 URI.pm 升级到 1.58 并不能解决问题;看来我需要 Perl 5.12 来完成这项工作。那么,回到我的问题,手动构建HTTP::Request 是实现%E8 的唯一可移植方式吗?
  • 我不知道,但我会走这条路,因为它很容易。我更新了答案以包含代码。
【解决方案2】:
use strict;
use warnings;
use utf8;  # Script is encoded using UTF-8.

use Encode                qw( encode );
use HTTP::Request::Common qw( POST );  # This is what ->post uses

my $req = POST('http://localhost:8080/', {
    text => encode("iso-8859-1", 'è'),
});

print($req->as_string());

给予

POST http://localhost:8080/
Content-Length: 8
Content-Type: application/x-www-form-urlencoded

text=%E8

您是否使用您正在传递«è»而不是它的 UTF-8 编码?如果我使用它的 UTF-8 编码,我会得到和你一样的结果。

...
my $req = POST('http://localhost:8080/', {
    text => encode("iso-8859-1", encode("UTF-8", 'è')),
});
...

给予

POST http://localhost:8080/
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

text=%C3%A8

【讨论】:

    【解决方案3】:

    对我自己的简短回答:只需将变量名(即“文本”)放在引号中,而不是将其写成裸词。

    $ua->post('http://localhost:8080/', {
        'text' => encode("iso-8859-1", 'è'),
    });
    

    比率:这种奇怪的行为是由以下因素共同造成的:

    • Perl bug #68812 导致将 UTF-8 内部标志设置为所有裸字。这已在最新的 Perl 版本 (>= 5.12) 中得到修复;
    • URI.pm 在转换字符之前将键连接到值(即“text=è”),因此如果键设置了内部标志,则始终将值提升为 UTF-8,即使您将值作为八进制传递也是如此。

    我不认为@Lumi 指出的关于URI.pm 使用\C 的错误对这个特定问题有影响。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-02
      • 2011-05-08
      • 1970-01-01
      • 2011-03-13
      • 2011-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多