0

我已经迁移到具有相同 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->content() 替换 $res->decoded_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}
      );
    }
  }
}
4

1 回答 1

2

作为一个近似值,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.

于 2013-10-13T17:53:39.120 回答