如何使用 Perl 的 DBI 处理 unicode?

Posted

技术标签:

【中文标题】如何使用 Perl 的 DBI 处理 unicode?【英文标题】:How can I handle unicode with Perl's DBI? 【发布时间】:2010-11-02 07:07:56 【问题描述】:

我的delicious-to-wp perl script 可以工作,但会为所有“怪异”字符提供更怪异的输出。 所以我尝试了

$description = decode_utf8( $description ); 

但这并没有什么不同。我想例如“上线”变成“上线”而不是“上线” 我如何在 Perl 中处理 unicode 以使其正常工作?

更新:我发现问题是设置 DBI 的 utf 我必须在 Perl 中设置:

my $sql = qqSET NAMES 'utf8';;
$dbh->do($sql);

这是我必须设置的部分,很棘手。谢谢!

【问题讨论】:

将您的代码提炼成可能仍然存在问题的最短脚本。将数据库排除在等式之外,以确定问题是否与 Perl 有关。想出一些其他人可以测试和调试的东西。 【参考方案1】:

默认情况下,驱动程序 Perl/mysql 处理二进制数据(至少我从 MySQL 5.1 和 5.5 的一些实验中得出了这一结论)。

在没有设置 mysql_enable_utf8 的情况下,我在向/从数据库写入/读取之前对字符串进行了编码/解码到 UTF-8。

不应依赖 perl 内部的字符串表示作为字节数组;请注意,内部的“utf8”不保证是标准的 UTF-8;相反,单字节编码不保证是 ISO-8859-1;确实对 UTF-8(而不是 'utf8')进行编码/解码。

还有一些 MySQL 的设置(比如上面的 SET NAMES,据我记得有一个客户端编码、一个连接编码和一个服务器编码,它们的交互对我来说不是很清楚,如果它们都没有的话相同的值)关于编码;将它们全部设置为 UTF-8,上面的方法对我有用。

【讨论】:

【参考方案2】:

启用 UTF8,当您像这样连接到数据库时:

my $dbh = DBI->connect(
    "dbi:mysql:dbname=db_name", 
    "db_user", "db_pass",
     RaiseError => 0, PrintError => 0, mysql_enable_utf8 => 1
 ) or die "Connect to database failed.";

这应该会根据需要为您提供带有 UTF8 标志的字符模式字符串。

来自DBI General Interface Rules & Caveats:

Perl 支持两种字符串:Unicode(内部为 utf8)和非 Unicode(如果强制采用编码,则默认为 iso-8859-1)。驱动程序应该接受这两种字符串,如果需要,将它们转换为正在使用的数据库的字符集。同样,当从数据库中获取非 iso-8859-1 的字符数据时,驱动程序应将其转换为 utf8。

以及来自DBD::mysql mysql_enable_utf8 的详细信息

此外,打开这个标志告诉 MySQL 传入的数据应该被视为 UTF-8。这只有在作为调用 connect() 的一部分时才会生效。如果在连接后打开标志,则需要发出命令 SET NAMES utf8 才能获得相同的效果。

【讨论】:

谢谢你。我尝试更新 Content-Type 和字符集....我思考了服务器端的编码和解码....所有这一切似乎我可能遗漏了一些东西。我的问题是输入字段中的单个撇号。在我的笔记本电脑上运行良好,但从我的 iphone 输入(顺便说一下,两个设备都使用 Chrome)我会得到一个甚至存在于数据库中的时髦字符。但是万岁你的答案。对我的 get_database_handle() 例程的简单修复解决了我的问题。向你致敬。【参考方案3】:

别管这个了:

binmode STDOUT, ":utf8";

使用时:

$dbh->do(qqSET NAMES 'utf8';);

否则您的输出将采用双 utf8 编码,导致无法读取的双字节字符! 我花了几个小时才弄清楚这一点..

【讨论】:

如果你说的是真的,那么 Perl 不知道从数据库中检索到的数据是 UTF-8 编码的,只是认为它是字节。当然 DBD::Mysql(或您使用的任何 DBD)正在为您解码数据库中的数据,在这种情况下,Perl 应该知道您有 Unicode 字符,并且当它们使用 utf8 层写入标准输出时,一切都会好起来的。如果你描述的是发生了什么,我建议你的 DBD 坏了。但是,查看最新的 DBD::Mysql 它确实执行“sv_utf8_decode(sv);”至少在一些数据上。 bohica,DBD::mysql正常提供字节,implicit decoding默认不开启。【参考方案4】:

术语

$dbh->do(qqSET NAMES 'utf8';);

绝对可以节省访问 utf-8 声明的数据库的时间,但请注意,如果您要对 从 db 获得的任何数据进行任何 perl 处理,存储它是明智的在 perl var 中作为 utf8 字符串使用,因为此操作不是隐式的。

$utfstring = decode('utf8',$string_from_db);

当然,为了正确处理 utf8 字符串(读取、打印、写入输出)的 i/o 处理,请记住设置

use open ':utf8';

binmode STDOUT, ":utf8";

后者对于打印 utf8 字符串至关重要。希望这会有所帮助。

【讨论】:

如何获取解码功能? Perl 显示错误,说找不到 decode() 例程。谢谢! 这解决了我的问题..有一个 perl cgi 脚本连接到 Postgres utf-8 数据库,该数据库使用 XHR 将 JSON 字符串返回给客户端。在我按照您的帖子中的建议添加my $final_utf8 = Encode::encode_utf8($treeJSON); print CGI->header('application/json;charset=UTF-8'); print "$final_utf8"; 之前,客户端上出现的字符都是乱码。请记住,您需要在 perl 脚本的顶部使用 use Encode; 才能使用它。 这个问题的答案都无法解决我的 UTF8 DB 问题,但这个问题解决了。谢谢!【参考方案5】:

值得注意的是,如果您运行的 DBD::mysql 版本足够新(3.0008 上),您可以执行以下操作:$dbh->'mysql_enable_utf8' = 1; 然后一切都为您解码()ed/encode()ed进出 DBI 的出路。

【讨论】:

对于像我这样的懒人,这是 dbic 变体:Schema->connect("dbi:mysql:".$dbdb, $dbuser, $dbpass, mysql_enable_utf8 => 1); 答案中的解决方案对我不起作用,但@al 上面评论中的解决方案。像魅力一样工作。 根据the documentation 可以在连接期间完成,这需要进一步的操作。如果您按照答案中的建议进行连接后,您还必须执行一个额外的命令:SET NAMES utf8.【参考方案6】:

它可能与 Perl 无关。检查以确保您在相关的 MySQL 表列中使用 UTF 编码。

【讨论】:

以上是关于如何使用 Perl 的 DBI 处理 unicode?的主要内容,如果未能解决你的问题,请参考以下文章

Perl 如何使用 DBI 模块更改帐户主机?

如何打印 Perl 的 DBI 填充占位符后执行的 SQL 查询?

Perl DBI:如何使用绑定值查看失败的查询?

如何检查 Perl 中的 DBI 查询是不是返回了多个记录?

如何正确表示 Perl 的 DBI 中的空格

Perl:在不死的情况下捕获错误