【问题标题】:What happens if DBI::mysql_enable_utf8 was not properly set?如果 DBI::mysql_enable_utf8 没有正确设置会发生什么?
【发布时间】:2017-04-16 06:34:52
【问题描述】:

我错过了这个声明,我最终在数据库中得到了有趣的字符:

mysql_enable_utf8 flag documentation

另外,打开这个标志告诉 MySQL 传入的数据 应被视为 UTF-8。这只有在作为一部分使用时才会生效 对 connect() 的调用。如果您在连接后打开标志, 您将需要发出命令 SET NAMES utf8 以获得相同的 效果。

这是部分代码:

use strict;
use utf8;
use open qw/:std :utf8/;

use DBI;

my $dbh = DBI->connect("...", $user, $pass) or die_report($@);
my $query;

$dbh->{'mysql_enable_utf8'} = 1; #this caused the problem, because it was added after the connect() statement.

$dbh->prepare("CREATE TABLE `$database`.`$table` (`id` INT(8) UNSIGNED NOT NULL AUTO_INCREMENT `response` MEDIUMTEXT) ENGINE = MYISAM DEFAULT CHARSET=utf8 COLLATE utf8_general_ci")->execute or die($@);

my $ua = LWP::UserAgent->new;
$response = $ua->get('http://example.com')->decoded_content;


$query = $dbh->prepare("INSERT INTO `$mysql_database`.`$mysql_table` (`id`, `response`) VALUES (?, ?)");
$query->execute($id, $response);

我需要了解$response 插入mysql 时会发生什么以及如何恢复损坏。 它会被双重编码吗?我可以用聪明的方式解决它吗?

【问题讨论】:

  • 提示:"`$database`.`$table`" 应该是 $dbh->quote_identifier($database, $table)
  • 请提供HEX(`response`)的损坏数据。
  • @ikegami,我认为原始表“中毒”,这就是我得到这个的原因。也许我写的某些包没有use utf8 或类似的东西,因为这就是我所拥有的:在表 ALTER 之前: 条目 1: Szabóné 537A6162C383C2B30000006EC383C2A9000000 条目 2: Szabóné 537A6162C3B36EC3A9 在表 ALTER 之后: 条目 1: Szabóné 537A6162C3B30000006EC3A9000000 条目 2: Szab 537A6162
  • 请问那些NUL是从哪里来的?这不仅仅是一个糟糕的mysql_enable_utf8 标志
  • 你是说你有一行包含537A6162C383C2B30000006EC383C2A9000000 和一行包含537A6162C3B36EC3A9 并且两者都应该是Szabóné?如果是这样,则并非所有行都已损坏。 537A6162C3B36EC3A9 是你应该拥有的。

标签: mysql perl encoding utf-8 character-encoding


【解决方案1】:

如果 Encode::is_utf8($response) 为真,并且连接的编码 (SHOW VARIABLES LIKE "character_set_client") 设置为 latin1,则以下方法可以解决问题:

UPDATE TheTable
   SET response = CONVERT(CONVERT(CONVERT(response USING latin1) USING BINARY) USING utf8)

您可以将 MySQL 字符串视为具有关联编码的字节,其中 BINARY 是一种特殊编码。下面解释了转换(无论是通过CONVERT 还是从存储到字段中)的工作原理:

  1. 从非二进制编码转换为另一种非二进制编码会转换底层字节。

  2. 从非二进制编码转换为二进制只需将与字节关联的编码更改为二进制(不更改字节)。

  3. 从二进制转换为非二进制编码只需将与字节关联的编码更改为新编码(不更改字节)。

因此,如果response 包含三个字符♡,则会发生以下情况:

C3 A2 E2 84 A2 C2 A1 [utf8] (♡)      `response` has an utf8 collation
     |
     |  CONVERT(_ USING latin1)        Changes how the string is encoded (as per #1)
     v
E2 99 A1 [latin1] (♡)
     |
     |  CONVERT(_ USING BINARY)        Changes the associated encoding (as per #2)
     v
E2 99 A1 [BINARY]
     |
     |  CONVERT(_ USING utf8)          Changes the associated encoding (as per #3)
     v
E2 99 A1 [utf8] (♡)
     |
     |  UPDATE SET `response` = _      Changes how the string is encoded (as per #1)
     v
E2 99 A1 [utf8] (♡)                    `response` has an utf8 collation

从技术上讲,由于response 有一个utf8 排序规则,您可以放弃最外面的CONVERT

【讨论】:

  • 提示:先做好备份!
  • 在问这个问题之前,我在一个重复的表上尝试了以下操作:ALTER TABLE d MODIFY description TEXT CHARACTER SET latin1; ALTER TABLE d MODIFY description TEXT CHARACTER SET binary; ALTER TABLE d MODIFY description TEXT CHARACTER SET utf8; 但我发现在以下字符 "Ã"[ this character存在于原始表中,在表的 ALTER 之后,该条目在此字符之后被砍掉 ]。因此,我认为在继续之前,我最好先问问后台到底发生了什么。
【解决方案2】:

53 7A 61 62 C383 C2B3 是“双重编码”。一种治疗方法是

CONVERT(BINARY(CONVERT(CONVERT(UNHEX('537A6162C383C2B3')
        USING utf8) USING latin1)) USING utf8) --> 'Szabó'

或者,更相关的是:

CONVERT(BINARY(CONVERT(CONVERT(BINARY(CONVERT('Szabó' USING latin1))
        USING utf8mb4) USING latin1)) USING utf8mb4) --> 'Szabó'

或者,更简单地说:

UPDATE tbl SET col = CONVERT(BINARY(CONVERT(col USING latin1)) USING utf8);

查看this 并搜索“double”。
请参阅this 并搜索“Perl”。
Fixes for various cases

【讨论】:

    猜你喜欢
    • 2019-09-27
    • 2013-02-21
    • 2016-10-31
    • 2011-08-06
    • 1970-01-01
    • 2011-03-19
    • 1970-01-01
    • 2016-07-30
    • 2011-12-27
    相关资源
    最近更新 更多