2017-10-13 82 views
0

我在Debian 8机器上使用Perl 5.20.2和MySQL 5.5.57。我最近发现MySQL的utf8表限于三字节表。因此我无法储存emojis。 所以,我尝试了utfmb4应该解决这个问题的表格。我改变了表从UTF8到utf8mb4从MySQL客户端中:在MYTABLEPerl MySQL utf8mb4问题/可能的bug

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; 

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

#!/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 | 
+--------+ 
|  | 
+--------+ 
+1

你应该真的使用占位符。 – simbabque

+0

您是否在提及准备报表?我通常这样做,但这似乎并不相关 – Marcus

+0

它是相关的,我花时间指出了它;-) – simbabque

回答

2

您告诉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

我接受了这个答案,因为它是'DBI'版本> = 4.041_01的方式。 Debian 8随3.0.17一起发布。对于该版本,当选择'mysql_enable_utf8 => 1'时解码工作,请参阅[本文](http://blogs.perl.org/users/mike_b/2016/12/dbdmysql-all-your-utf-8-bugs -are-belong-to-us.html) – Marcus

0

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

感谢您的建议,但不幸的是,我将不得不在我的应用程序中重新访问超过1k分贝的查询。更糟的是,他们将不得不被测试。 – Marcus

0

"\x{1F600}";是 “统一”,而不是 “UTF-8”。它们是相关的,但它们是而不是相同的编码。

您需要UTF-8(正如非mysql世界所称的那样)和utf8mb4(正如MySQL所称的那样)。

是十六进制F09F9880(in utf8mb4);如果你通过CHARACTER SET latin1(“Mojobake”)转换😀

请运行SELECT HEX(object) ...看看你是否得到这些4个十六进制字节或其他东西。然后我们将知道是否要关注INSERTSELECT

你说的“实际产出” - 但这是什么?一个网页?它是否配置为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; 
+0

这是控制台输出,它与Ubuntu和W10/Putty 0.7一起开箱即用。 Win7/Putty 0.7并不适用,尽管我还没有试过'chcp'ing – Marcus

+0

比较'my.cnf'。您可能会发现不同的默认值。 MySQL_的哪些版本? –