【问题标题】:How to pass Unicode correctly to Perl's DBI layer?如何正确地将 Unicode 传递给 Perl 的 DBI 层?
【发布时间】:2016-01-20 15:40:23
【问题描述】:

我有以下用于 Sqlite 数据库的简单 Perl 包装器:

#! /usr/bin/perl

use strict;
use warnings;
use DBI;
use Data::Dumper;

my $sql = shift;

my $dbh = DBI->connect(
    "dbi:SQLite:dbname=data.sqlite3",
    "", # no user
    "", # no pw
    {
        RaiseError => 1,
        sqlite_unicode => 1
    },
) || die $DBI::errstr;

my $sth = $dbh->prepare($sql);
$sth->execute();

print Dumper ($sth->fetchall_arrayref({}));

$sth->finish();
$dbh->disconnect();

虽然我设置了sqlite_unicode标志as explained in the documentation,但我无法执行包含Unicode字符的查询:

$ ./sqlite.pl "select * from person where lastname = 'Schütte'"
$VAR1 = [];

当我屏蔽 'ü' 时,它似乎可以工作,虽然我不确定 \x{fc} 是否意味着 Latin 1 FC or Unicode U+00FC

$ ./sqlite.pl "select * from person where lastname like 'Sch%tte'"
$VAR1 = [
          {
            'id' => 8,
            'firstname' => undef,
            'lastname' => "Sch\x{fc}tte"
          }
        ];

当我使用 Sqlite 命令行工具执行相同操作时,它可以正常工作:

$ sqlite3 data.sqlite3 "select * from person where lastname = 'Schütte'"
8||Schütte

我是否忘记告诉 DBI 层支持 Unicode 字符?

我的本​​地编码是 UTF-8:

$ locale
LANG=de_DE.utf8
LANGUAGE=
LC_CTYPE="de_DE.utf8"
LC_NUMERIC="de_DE.utf8"
LC_TIME="de_DE.utf8"
LC_COLLATE="de_DE.utf8"
LC_MONETARY="de_DE.utf8"
LC_MESSAGES="de_DE.utf8"
LC_PAPER="de_DE.utf8"
LC_NAME="de_DE.utf8"
LC_ADDRESS="de_DE.utf8"
LC_TELEPHONE="de_DE.utf8"
LC_MEASUREMENT="de_DE.utf8"
LC_IDENTIFICATION="de_DE.utf8"
LC_ALL=

【问题讨论】:

    标签: perl sqlite


    【解决方案1】:

    你需要问自己的第一个问题是:

    应该对查询进行编码(如果是,采用哪种编码),还是应该对其进行解码(即 Unicode 代码点)?

    这应该基于sqlite_unicode,但事实并非如此。这只影响返回的字符串。我怀疑 DBD::SQLite 与大多数/所有 DBD 一样,都存在 Unicode 错误,这意味着问题的答案基于字符串存储方式的内部细节。

    在 UTF-8 系统中,它通常“正常工作”,我很惊讶它在这里没有。请尝试

    use Encode qw( decode_utf8 );
    
    my $sql = decode_utf8(shift);
    

    如果这不起作用,请提供以下输出(在将查询移出@ARGV 之前):

    use Devel::Peek qw( Dump );
    Dump($ARGV[0]);
    

    当我屏蔽 'ü' 时,它似乎可以工作,虽然我不确定 \x{fc} 是指拉丁语 1 FC 还是 Unicode U+00FC

    这个问题没有任何意义。在 iso-latin-1 和 Unicode 中,FC 是“ü”。我相信你实际上是在问

    返回的字符串是使用 iso-latin-1 编码的,还是解码后的字符串(即 Unicode 代码点)?

    sqlite_unicode 是后者。您需要对输出进行编码。你可以这样做:

    use open ':std', ':encoding(UTF-8)';
    

    【讨论】:

    • 做明确的decode_utf8 有帮助,但我不知道为什么。在语言环境设置为 UTF-8 且 DBI 目标也需要 UTF-8 的系统上,decode_utf8 期间会发生什么?
    • 就是这样。这不是 UTF-8 DBI 想要的
    • 我几乎整个上午都在阅读关于晦涩难懂的 Perl Unicode 问题。到目前为止我所理解的是,decode_utf8 启用了 Perl 的 UTF-8 标志。这就是 DBI 似乎想要的。也许原因可能是 Perl 源代码的默认值是过去的 Latin-1 并且大多数 SQL 语句是 Perl 源代码的一部分。这可能是 DBI 将每个字符串都视为 Latin-1 的原因。这将使得有必要打开 UTF-8 标志以防止默认设置。但这只是猜想。问题似乎是特定于 Sqlite 的。 MySQL 似乎没有问题。
    • 解码后的字符串应该是它所除外的。它实际上比这更复杂,但我今天不想详细介绍 The Unicode Bug。
    猜你喜欢
    • 2011-08-11
    • 2011-12-05
    • 2019-11-05
    • 2017-06-22
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 2011-10-06
    相关资源
    最近更新 更多