0

同じ freebsd システムの新しいホスティング プロバイダに移行しましたが、perl スクリプトの 1 つが正しく動作しなくなりました。

外部の 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() に置き換えると、すべてが機能します。おそらく、ダウンロードしているページのヘッダーに文字セットがないためです。

cp1251 のように見えますが、そうではないというような方法で、decoded_content がどのように文字列を台無しにするのか、私はまだ理解していません。多分いくつかの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 は与えられた raw バイトまたは 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 に答える