2012-02-16 36 views
12

我将要完成将数据库从Latin1转换为UTF-8的繁琐和棘手的任务。如何检测Latin1编码列中的UTF-8字符 - MySQL

在这一点上,我只是想检查我的表中存储了哪些数据,因为这将决定我应该使用什么方法来转换数据。

具体来说,我想检查在Latin1列中是否有UTF-8字符,那么最好的方法是什么?如果只有几行受到影响,那么我可以手动修复此问题。

选项1.执行MySQL转储并使用Perl搜索UTF-8字符?

选项2.使用MySQL CHAR_LENGTH查找具有多字节字符的行? 例如SELECT name FROM clients WHERE LENGTH(name) != CHAR_LENGTH(name); 这够了吗?

此刻我已将我的Mysql客户端编码切换为UTF-8。

+0

根据定义,您不能在Latin1列中存储UTF-8数据。谨慎地为您的问题提供更多的背景信息? – deceze 2012-02-16 02:07:45

+0

UTF-8多字节字符都大于128.但实际上有**无法**来确定字符的意图是什么:我认为你想要问的是“我可以检测到非ASCII字符一个Latin1编码列“。显然,由于字节序列0xF0 0x53意味着UTF-8和拉丁语1中的两个不同的东西,所以即使找到它,也不知道它是哪一个... – Borealid 2012-02-16 02:09:12

+3

@deceze您可能无意中将UTF-8数据存储在LATIN1中列,因为LATIN1是一个8位字符集。它只是看起来像一个混乱的编码错误。 – tadman 2012-02-16 03:38:25

回答

37

与时区一样,字符编码是问题的常见来源。

您可以做的是查找任何“高-HASCII”字符,因为它们是LATIN1重音字符或符号,或者是UTF-8多字节字符中的第一个。除非你欺骗一点,否则说出这种区别并不容易。

为了弄清楚什么编码是正确的,你只需要SELECT两个不同的版本,并在视觉上进行比较。这里有一个例子:

SELECT CONVERT(CONVERT(name USING BINARY) USING latin1) AS latin1, 
     CONVERT(CONVERT(name USING BINARY) USING utf8) AS utf8 
FROM users 
WHERE CONVERT(name USING BINARY) RLIKE CONCAT('[', UNHEX('80'), '-', UNHEX('FF'), ']') 

这是由异常复杂,因为MySQL的正则表达式引擎似乎忽略的东西像\x80并使得有必要使用UNHEX()方法来代替。

这产生的结果是这样的:

latin1    utf8 
---------------------------------------- 
Björn    Björn 
+0

对迟到的回应和模糊的初始问题抱歉。获得这个答案是因为它或多或少帮助我检测可能意图是UTF8字符的字符。 Upvoted deceze的答案,因为它包含我在数据库中其他地方的情况 – dinie 2012-02-23 02:07:34

+0

真棒 - 这个小块帮助我解决了utf8编码数据插入到utf8表时被解释为latin1的问题,因为我通过mysql CLI输入了它。虽然有趣,但因为系统设置为UTF8,所以在输入和选择时看起来很好(只是在关联的网站上解码和呈现时不会)。 – Kasapo 2012-10-25 14:36:09

+1

有时候,如果您从两个连接读取和写入数据时,完全相同的错误配置,它会奇迹般地工作。有时候,两次错误确实是对的。 – tadman 2012-10-25 16:56:50

0

我将在数据库和grep的转储所有有效UTF8序列。从哪里拿到它取决于你得到什么。关于识别无效的UTF8有很多问题。你基本上可以逆转逻辑。

编辑:基本上,任何由7位ASCII组成的字段都是安全的,任何包含无效UTF-8序列的字段都可以假定为Latin-1。剩下的数据应该被检查 - 如果你幸运的话,少数几个明显的替换将会解决绝大多数问题(用Latin-1代替ö等)。

+1

[This answer](http://stackoverflow.com/a/7302465/333340)包含一个相当长的可能的坏组合列表。 – Synchro 2013-04-05 07:03:42

8

因为你的问题不是完全清楚,让我们假设一些情景:

  1. 迄今为止连接错误:你一直连接到数据库使用latin1编码不正确,但存储UTF-8数据在数据库中(列的编码在这种情况下是不相关的)。这是我描述的情况here。在这种情况下,很容易修复:通过latin1连接将数据库内容转储到文件。这会将错误地存储的数据转换为错误地正确存储的UTF-8,这是迄今为止它的工作方式(请阅读上述关于血腥细节的文章)。然后,您可以通过正确设置的utf8连接将数据重新导入数据库,并将其存储为应有的数据。
  2. 迄今为止错误的列编码:通过utf8连接将UTF-8数据插入到latin1列中。在那种情况下忘记它,数据就消失了。任何非latin1字符都应该替换为?
  3. 迄今为止一切都很好,此后增加了对UTF-8的支持:您已将Latin-1数据正确存储在latin1列中,并通过latin1连接插入,但希望将其扩展为也允许UTF-8数据。在这种情况下,只需将列编码更改为utf8。 MySQL将为您转换现有的数据。然后,只要确保在插入UTF-8数据时将数据库连接设置为utf8。
+0

如果多个客户端一直在添加数据,并且其中一些客户端认为他们应该提交utf8,那么您将会得到一个邪恶的混合,而这个混淆基本上需要手动进行。这并不意味着你不能自动完成部分流程,实际上大多数情况下都可以在没有人为干预的情况下决定。 – tripleee 2012-02-16 06:10:38

+0

的确如此,但是你真的完全是foobar。在尝试回答这种情况之前,OP将需要提供更多有关实际问题的信息。 – deceze 2012-02-16 06:13:09

+0

对于情况1,对我来说,这个命令是'mysqldump --default-character-set = latin1 -u user -p database'。然后我必须进入转储文件并将'SET NAMES latin1'改为'utf8'。然后重新导入转储文件和所有固定的。 – James 2017-02-16 23:02:39

3

a script on github来帮助这种事情。

+1

这个脚本对我来说工作得非常好,我做了一些改进,使它更快更灵活。我也有[分支](https://github.com/Synchro/mysql-convert-latin1-to-utf8/tree/utf8mb4),它转换为MySQL ['proper'utf8mb4 charset](http:// mathiasbynens.be/notes/mysql-utf8mb4)。 – Synchro 2013-04-05 07:03:26

+0

这个脚本的工作..仍然不明白它是如何工作的..需要经过它..从拉丁1 - utf8几乎无痛地移动,不得不添加'mysql_set_charset(“utf8”);'为PHP使用它后立即那。 – 2014-09-07 14:36:11