我已经迁移到一个新的托管服务提供商,使用相同的freebsd系统,我的一个perl脚本停止正常工作。
它从外部https站点下载数据并将其存储在mysql db中。数据采用 cp1251 编码,相同的编码采用 mysql 库、表和连接。来自 my.cnf:
character-set-server=cp1251
collation-server=cp1251_general_ci
init-connect="SET NAMES cp1251"
从perl脚本连接到mysql时:
$dbh->do('SET CHARACTER SET cp1251');
所以,我正在用
$ua = new LWP::UserAgent;
....
$res = $ua->get(....)
$s = $res->decoded_content();
然后脚本将解析此$s并将结果插入到mysql中。当它发生时,编码已损坏!
我发现的有趣的事情是,如果我只是将这些数据写入文本文件,然后从该文件中读取它并将其插入 mysql - 它没有损坏!
当我查看此文本文件时,我看到数据采用 cp1251 编码。
自上次托管以来发生了哪些变化:
Perl:从 5.10.1 到 5.14.4
libwww: 从 5.835 到 6.05
MySQL 服务器与 5.1 相同
更新:哇,刚刚发现了一些东西。如果我将 $res->decoded_content() 替换为 $res->content(),一切正常。也许这是因为我正在下载的页面标题中没有字符集。
我仍然不明白decoded_content如何以这种方式弄乱字符串,它看起来像 cp1251 但事实并非如此。也许是一些utf标志?请帮忙。
UPDATE2:这是脚本(主要部分):
#!/usr/bin/perl
use POSIX qw(strftime);
use LWP::UserAgent;
use HTTP::Headers;
use HTTP::Cookies;
use Digest::MD5 qw(md5_hex);
use DBI;
use common::sense;
no utf8;
no strict;
$ua = new LWP::UserAgent;
$hh = HTTP::Headers->new(
User-Agent => 'Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0',
Accept => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
Accept-Language => 'en-us,en;q=0.7,ru;q=0.3',
Accept-Encoding => 'gzip, deflate',
Connection => 'keep-alive',
);
$ua->default_headers( $hh );
$ua->cookie_jar({});
$ua->timeout(20);
YMoney();
sub YMoney {
$res = $ua->get('...');
$res = $ua->post('...');
...
$res = $ua->get("...");
$s = $res->decoded_content();
@list = reverse split("n", $s);
$dbh = DBI->connect("DBI:mysql:database=orders;host=localhost;port=3306", ....);
$dbh->do('SET CHARACTER SET cp1251');
for $line (@list) {
next if ($line !~ /^+;/);
@pay{'data', 'amount', 'comment'} = map { s/"+//g; $_ } (split(';', $line))[1, 2, 5];
$pay{hash} = md5_hex( join('', @pay{'data', 'amount', 'comment'}) );
$id = $dbh->selectrow_array("SELECT id FROM ymoney WHERE hash = ?", {}, $pay{hash});
if (!$id) {
$dbh->do("INSERT INTO ymoney (operator, hash, data, amount, comment) VALUES ('yandex', ?, ?, ?, ?)", {},
$pay{hash}, DB_Date($pay{data}), DB_Amount($pay{amount}), $pay{comment}
);
}
}
}
作为一个近似值,Perl 要么对你给它的原始字节进行操作,要么对 Unicode 代码点进行操作。在处理文本数据时,后者更有用。但这意味着您必须解码所有输入,并对输出进行编码。
__________ | _______________
WEB PAGE | __|__ | _______
-------- ------------- L | YOUR APP -------------- DATA |
/ -------- / 〉 encoded data 〉 W | 〉 encoded data 〉 BASE |
/ -------- / /-------------/__P _| codepoints /--------------/______|
__________ | |_______________|
当你使用decoded_content
时,LWP非常好,可以直接给你代码点。未解码的content
没有用:它可能已压缩、具有传输编码或可能位于意外的字符集中。
但这意味着现在,您必须再次对该文本进行编码。如果服务器需要二进制 blob,您可以显式执行此操作,也可以让 DBI 为您解决这个问题 - 不需要set character set
。
TL;DR:删除任何编码黑客攻击,除非您知道自己在做什么。如果您遵循最佳实践,一切都应该会很好。否则,请使用 Encode
进行自己的编码。