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;
我在 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;