Perl MySQL utf8mb4问题/可能的错误

时间:2017-10-13 10:02:47

标签: mysql perl utf8mb4

我在Debian 8机器上使用Perl 5.20.2和MySQL 5.5.57。我最近发现MySQL的utf8表仅限于三字节字符串。因此,我无法存储表情符号。 所以,我尝试了utfmb4表来解决这个问题。我在mysql客户端内部将表从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;

在mytable中存储数据似乎有效,至少我可以在phpMyAdmin中看到预期的表情符号。但是,当从表中读取时,我收到一个包含3个不可打印的字符的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 |
+--------+
|       |
+--------+

3 个答案:

答案 0 :(得分:3)

你告诉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;

答案 1 :(得分:1)

解决方法是让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'});

答案 2 :(得分:0)

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

你需要UTF-8(非mysql世界称之为)和utf8mb4(正如MySQL所称)。

是十六进制F09F9880(在utf8mb4中);如果您通过😀(“Mojobake”)进行转换,则为CHARACTER SET latin1

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

你说“实际输出” - 但这是什么?一个网页?是否为UTF-8配置了?或者是其他东西?如果是命令行窗口,请确保将其设置为UTF-8。在Windows中,通过chcp 65001完成。

你提到了

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

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

SET NAMES utf8mb4;