[英]Why are Perl Hashkey names from mySQL Result are not UTF8?
我有点困惑,找不到答案。 所以我希望能在这里找到帮助。
我有一个小 mysql 表:
CREATE TABLE `datatable` (
`Artikelnummer` varchar(10) NOT NULL,
`Bezeichnung` varchar(25) NOT NULL,
`Länge` mediumint(6) DEFAULT NULL,
`Breite` mediumint(6) DEFAULT NULL,
`Höhe` mediumint(6) DEFAULT NULL,
`Vö-Datum` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `datatable`
(`Artikelnummer`, `Bezeichnung`, `Länge`, `Breite`, `Höhe`, `Vö-Datum`)
VALUES ('123456', 'Hängematte', 12, 20, 35, '2020-08-31');
ALTER TABLE `datatable`
ADD PRIMARY KEY (`Artikelnummer`);
我的 perl 测试脚本是:
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
use CGI qw (:standard);
use DBI;
use Data::Dumper;
use DBIx::Log4perl;
binmode(STDOUT, ':utf8');
my $dbh = '';
if ($dbh = DBIx::Log4perl->connect("DBI:mysql:test","user","password",{
RaiseError => 1,
PrintError => 1,
mysql_enable_utf8 => 1
}))
{
$dbh->do('SET NAMES utf8');
$dbh->do('SET CHARSET utf8');
my $sql_query
= 'SELECT * FROM datatable WHERE Artikelnummer = ?';
my $out = $dbh->prepare($sql_query);
$out->execute( "123456" )
or die 'Select-Fehler: '.$dbh->errstr();
my $Content = $out->fetchrow_hashref();
$out->finish();
# Test 1
if ($Content->{'Bezeichnung'} eq 'Hängematte') {
print "Test 1: Content Hängematte found!\n";
}
# Test 2
if (defined $Content->{'Höhe'}) {
print "Test 2: Key Höhe found!\n";
} else {
print "Test 2: Key Höhe not found!\n";
}
# Hack: Hardcore BadHack!!!
$Content->{'Höhe'} = $Content->{'Höhe'};
$Content->{'Länge'} = $Content->{'Länge'};
$Content->{'Vö-Datum'} = $Content->{'Vö-Datum'};
# Test 3
if (defined $Content->{'Höhe'}) {
print "Test 3: Key Höhe found!\n";
} else {
print "Test 3: Key Höhe not found!\n";
}
print Dumper($Content);
} else {
print "Verbindungsfehler: " . $dbh->errstr();
}
exit(0);
output 是:
Log4perl: Seems like no initialization happened. Forgot to call init()?
Test 1: Content Hängematte found!
Test 2: Key Höhe not found!
Test 3: Key Höhe found!
$VAR1 = {
'Vö-Datum' => '2020-08-31',
'Länge' => 12,
'Höhe' => 35,
'Höhe' => 35,
'Breite' => 20,
'Länge' => 12,
'Bezeichnung' => "H\x{e4}ngematte",
'Vö-Datum' => '2020-08-31',
'Artikelnummer' => '123456'
};
首先,请忽略 Log4perl 警告,因为它只是演示我的问题的示例代码。 ;-)
我的问题是,我如何在示例中获得正确的编码哈希键而没有这种糟糕的黑客攻击,或者其他被问到为什么我在 hash 中获得的表格行不是 utf8 编码的?
我使用 perl 5.25、DBIx::Log4perl 0.26、DBI 1.642 和 mySQL 服务器 5.7.31
首先,让我们明确一点,这不是数据库本身的问题。 即使使用正确格式的表格也会出现问题,如下所示:
mysql> DESCRIBE MyTable;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| Höhe | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
1 row in set (0.00 sec)
问题完全出在接收端。
默认情况下,DBD::mysql 返回按照客户端字符集 ( SET NAMES
) 有效编码的每个字符串。
可以在某种程度上使用mysql_enable_utf8
来覆盖它。 具体来说,它执行以下操作:
SET NAMES utf8
或等效项(仅当mysql_enable_utf8
设置为connect
的一部分时)。这意味着您可以:
后者包括列名。 您可以使用以下内容对fetchrow_hashref
返回的 hash 进行解码。
sub _d { my ($s) = @_; utf8::decode($s); $s }
sub decode_keys {
my ($hash) = @_;
return { map { _d($_) => $hash->{$_} } keys(%$hash) };
}
重现问题并演示修复:
use 5.014;
use warnings;
use utf8; # Source saved as UTF-8
use open ':std', ':encoding(UTF-8)'; # Terminal expects UTF-8
use Data::Dumper qw( Dumper );
use DBI qw( );
sub _e { my ($s) = @_; utf8::encode($s); $s }
sub _d { my ($s) = @_; utf8::decode($s); $s }
sub decode_keys { { map { _d($_) => $_ } keys(%{ $_[0] }) } }
my $host = ...;
my $db = ...;
my $user = ...;
my $password = ...;
my $dbh = DBI->connect(
"dbi:mysql:$db;host=$host",
$user, $password,
{
RaiseError => 1,
PrintError => 0,
PrintWarn => 1,
mysql_enable_utf8 => 1, # Or mysql_enable_utf8mb4 => 1
},
);
$dbh->do(_e('
CREATE TEMPORARY TABLE `MyTable` (
`Höhe` VARCHAR(10) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8
'));
$dbh->do(_e('
INSERT INTO `MyTable`
SET `Höhe`="Höhe"
'));
my $rows = $dbh->selectall_arrayref(
_e('SELECT * FROM `MyTable`'),
{ Slice => {} },
);
{
local $Data::Dumper::Useqq = 1;
print(Dumper($rows));
}
for my $row (@$rows) {
for my $col_name (keys(%$row)) {
say "$col_name: $row->{$col_name}";
}
}
$_ = decode_keys($_) for @$rows;
{
local $Data::Dumper::Useqq = 1;
print(Dumper($rows));
}
for my $row (@$rows) {
for my $col_name (keys(%$row)) {
say "$col_name: $row->{$col_name}";
}
}
Output:
$VAR1 = [
{
"H\303\266he" => "H\x{f6}he"
}
];
Höhe: Höhe
$VAR1 = [
{
"H\x{f6}he" => "H\x{f6}he"
}
];
Höhe: Höhe
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.