【问题标题】:Perl CGI::Session MySQL thaw utf-8 dataPerl CGI::Session MySQL 解冻 utf-8 数据
【发布时间】:2015-09-11 23:42:25
【问题描述】:

我正在使用 CGI::Session 将 utf-8 会话数据存储到 MySQL DB 中,并使用 YAML 作为序列化程序,效果很好。问题是在解冻时,会话数据似乎没有被解码为 Perls 内部格式,尽管传递给会话构造函数的数据库句柄被配置为这样做。 解冻后立即在每个会话参数上手动应用 decode_utf8 确实可以解决此问题,但这很不方便。

这是我的设置:

use warnings;
use strict;

$dbh->{'mysql_enable_utf8'} = 1;
$dbh->do('set names utf8');

$session = CGI::Session->new("driver:MySQL;serializer:yaml", undef, {
  TableName   => "session",
  IdColName   => "id",
  DataColName => "data",
  Handle      => $dbh,
} ) or die CGI::Session->errstr;

# column 'data' of table 'session' is of type mediumtext, has charset utf8 and collation utf8_unicode_ci

示例 sn-p:

binmode(STDIN, ":encoding(utf8)");
binmode(STDOUT, ":encoding(utf8)");

if( !defined $session->param('first_name') ){
  $session->param('first_name','jörg');
}

print $session->param('first_name');

第一次运行时会输出:'jörg'

在第二次运行时(名称现在来自会话表):'jörg'

如上所述,这将解决它:

binmode(STDIN, ":encoding(utf8)");
binmode(STDOUT, ":encoding(utf8)");

if( !defined $session->param('first_name') ){
  $session->param('first_name','jörg');
} else {
  $session->param('first_name',decode_utf8($session->param('first_name')));
}

print $session->param('first_name');

(我确实使用完全相同的数据库句柄将“first_name”存储在“person”表中,并且从那里读取/写入/输出可以完美运行。)

那么,为什么数据不能通过 CGI::Session 正确解码为 Perls 格式,或者我如何告诉 CGI::Session 这样做呢? 此行为还会导致序列化程序 Dumper、Storable 和 FreezeThaw 在尝试解冻之前已损坏的数据时崩溃。 例如。当会话数据不是 Perls 内部格式时,Dumper 只会在 'jörg' 的 'ö' 处剪切会话数据。

非常感谢您对此提供的任何提示,请原谅我用词不当。我只是想弄清楚 unicode-in-perl 问题。 (是的,我已经阅读了许多通用指南和操作方法,但遗憾的是找不到关于 session-mysql 主题的任何内容。)

最好的问候, 托马斯

根据simbaque更新(感谢提示),但确实不是这里的问题。

【问题讨论】:

  • 您的代码在示例new CGI::Session 中看起来有点过时。你应该添加 use strictuse warnings 虽然我不认为这是这里的问题。

标签: mysql perl session utf-8


【解决方案1】:

在您的示例中,我看到您将输出设置为 UTF8,但您是否曾经将输入设置为 UTF8? CGI::Sessionhas a warning 关于 UTF8,在该部分中,它讨论了将输入和输出设置为 UTF8。如果您包含binmode STDIN, ":encoding(utf8)";,您的程序会发生什么?

【讨论】:

  • 我实际上已经设置了binmode STDIN, ":encoding(utf8)";,抱歉我没有将它添加到sn-p,现在更新它。但这似乎不是因为会话参数是通过数据库句柄而不是通过 STDIN 初始化的,对吧?
  • 细看CGI::Session,不知道CGI::Session::Serialize::yaml支持UTF8。支持 YAML 发布的最后一个 CGI::Session 版本是 4.10,那是在 06 年 3 月。有什么理由必须使用 YAML?升级 CGI::Session 并尝试 freezethaw 或 storable 是否可行?
  • 数据列更适合人类可读,因此选择 YAML 或默认序列化程序 Data::Dumper。你关于前者可能已经过时的暗示让我再次看看 Dumper。这里的问题具有相似的性质。 utf8 字符被 dumper 转义并在解冻时恢复几乎正确但没有 perls utf8 标志。这会使转储程序损坏(cutof)各个 unicode 字符上的会话数据。找到了解决方法,也按照您的建议使用 storable ,作为答案发布,因为它可能对其他人有所帮助,但并不能真正回答问题。
【解决方案2】:

听起来表格列没有声明CHARACTER SET utf8

【讨论】:

  • 很遗憾,事实并非如此。数据列声明为CHARACTER SET utf8 COLLATE utf8_unicode_ci
  • 让我们进行一些调试...从 Perl 中,每一步都转储 jörg 的十六进制。 jörg 正确编码为 utf8 是十六进制 6AC3B67267jörg 编码为 latin1 是十六进制 6AF67267; “双重编码”:6AC383C2B67267。我不知道在 Perl 中获取十六进制的详细信息,但它可能涉及unpack('H*', $text)More latin1 & utf8 encodings
  • 好的,这就是我得到的: 在 perl 中设置变量:'jörg' 6A F6 72 67 从会话表中解冻后:'jörg' 6A C3 B6 72 67 在数据库中(在会话中-> 数据列以及 person->first_name 列)utf8 编码保持不变:6A C3 B6 72 67 似乎名称在写入会话表时正确编码为 utf8,解冻时读取时不会解码,但对于某些原因在下一次写入会话表时也没有得到双重编码,这让我很难过。
  • 正确的做法是使用 100% utf8。这将涉及弄清楚如何在 utf8 中获取原始字符串。它从哪里来的? “第二个选择”是弄清楚 Yaml 如何(或为什么)进行转换。恐怕我也没有任何线索。
【解决方案3】:

没有准确回答原始问题,但对于其他有类似问题的人来说,这两个选项可能是有效的解决方法:

  1. 使用默认序列化程序 Data::Dumper 并强制它使用纯 perl 版本似乎能够在设置 utf8 标志的情况下根据需要恢复数据。缺点是它应该比默认使用的 perl/XS 版本慢很多。

    $Data::Dumper::Useperl = 1;

  2. 使用可存储的序列化程序并将数据列更改为mediumblob 类型。这可能完全绕过了整个编码问题,因为您只是从数据库中读取和写入二进制数据。但是数据列不再是人类可读的了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    相关资源
    最近更新 更多