Perl MySQL utf8mb4 问题/可能的错误

Perl MySQL utf8mb4 issue / possible bug

我在 Debian 8 机器上使用 Perl 5.20.2 和 MySQL 5.5.57。我最近发现 MySQL 的 utf8 table 仅限于三字节字符。结果我无法存储表情符号。 所以,我尝试了 utfmb4 tables 应该可以解决这个问题。我从 mysql 客户端内部将 table 从 utf8 更改为 utf8mb4:

ALTER DATABASE `mydb` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE `mydb`.`mytable` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `mydb`.`mytable` CHANGE `object` `object` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

在我的 table 中存储数据似乎可行,至少我可以在 phpMyAdmin 中看到预期的表情符号。但是,当从 table 读取时,我收到一个包含 3 个 unprintable 字符的 4 个字符的结果。以下程序应该打印相同的表情符号两次:

#!/usr/bin/perl

use 5.10.1;
use warnings;
use strict;
use DBI;

binmode(STDOUT, ':utf8');

my $object = "\x{1F600}";
my $hd_db  = DBI->connect('DBI:mysql:mydb:localhost', 'user', 'password');
$hd_db->do('SET NAMES utf8mb4');

# cleanup
my $delete = $hd_db->prepare("DELETE FROM mytable");
$delete->execute;

my $insert = $hd_db->prepare("INSERT INTO mytable (object) VALUES ('" . $object . "')");
$insert->execute;
my $select = $hd_db->prepare("SELECT * FROM mytable");
$select->execute;
my $row    = $select->fetchrow_hashref;

say $object;
say $row->{'object'};

预期输出:



实际输出:


�

对我来说似乎是个错误。有什么解决方法的建议吗?

编辑:从 mysql 客户端中选择数据也会显示预期的表情符号

mysql> SET SESSION CHARACTER_SET_CLIENT = utf8mb4;
mysql> SET SESSION CHARACTER_SET_RESULTS = utf8mb4;
mysql> SELECT * FROM mytable;
+--------+
| object |
+--------+
|       |
+--------+

解决方法是让 MySQL 将所有内容都视为字节并在您的应用程序中进行编码。

use Encode qw(encode decode);

my $object = "\x{1F600}";
my $hd_db  = DBI->connect('DBI:mysql:mydb:localhost', 'user', 'password');
$hd_db->do('SET NAMES latin1');

...

my $insert = $hd_db->prepare("INSERT INTO mytable (object) VALUES ('" . 
    encode("UTF-8",$object) . "')"); # or equiv statement with placeholders
$insert->execute;

...

my $select = $hd_db->prepare("SELECT * FROM mytable");
$select->execute;
my $row    = $select->fetchrow_hashref;
say $object;
say decode("UTF-8",$row->{'object'});

您告诉MySQL使用UTF-8进行通信,但您还需要告诉DBD::mysql解码数据(或自己做)。

你想要

my $dbh = DBI->connect('DBI:mysql:mydb:localhost', 'user', 'password', {
   mysql_enable_utf8mb4 => 1,
})
   or die($DBI::errstr);

相当于

my $dbh  = DBI->connect('DBI:mysql:mydb:localhost', 'user', 'password')
   or die($DBI::errstr);

$dbh->do('SET NAMES utf8mb4')
   or die($dbh->errstr);

$dbh->{mysql_enable_utf8mb4} = 1;

"\x{1F600}"; 是 "Unicode",而不是 "utf8"。它们是相关的,但它们不是相同的编码。

您需要 UTF-8(非 mysql 世界称呼它)和 utf8mb4(MySQL 称呼它)。

</code> 是十六进制 <code>F09F9880 (在 utf8mb4 中);如果通过 CHARACTER SET latin1 ("Mojobake")

转换为 😀

请运行 SELECT HEX(object) ... 看看您是否得到了这 4 个十六进制字节或其他内容。然后我们就知道是关注INSERT还是关注SELECT.

你说 "actual output" -- 但这是哪里?网页?是否为 UTF-8 配置?或者是其他东西?如果它是您的命令行 window,请确保它已设置为 UTF-8。在 windows 中,这是通过 chcp 65001 完成的。

你提到了

mysql> SET SESSION CHARACTER_SET_CLIENT = utf8mb4;
mysql> SET SESSION CHARACTER_SET_RESULTS = utf8mb4;

这只是需要设置的 3 个中的 2 个。最好简单地做

SET NAMES utf8mb4;