如何将被UTF-8双重编码的字符串解码为简单的UTF-8?

6
我有一个非常大的MySQL表,其中的行被UTF-8编码了两次。例如,“Újratárgyalja”被存储为“Újratárgyalja”。
MySQL .Net连接器以这种方式下载它们。我尝试了许多System.Text.Encoding.Convert()的组合,但都没有起作用。
发送set names 'utf8'(或其他字符集)无法解决此问题。
如何将它们从双重UTF-8解码为UTF-8?

1
我必须点赞这个问题,主要是因为它促使了Alex的回答,非常棒。此外,在一般情况下,编码可能是一个棘手的问题,就像我在自己的机器上尝试Alex的答案时重新发现的那样。我有一种感觉,他的方法可能会在其他互操作性环境中提供帮助(至少作为线索)。 - John Y
3个回答

8

这是一个奇怪的问题,但我认为我可以通过合适的UTF-8和Latin-1混合来复制它(不仅仅是两次使用UTF-8而没有在其中插入Latin-1错误)。以下是整个奇怪的往返过程,“去了又回来”的示例(Python 2.*或IronPython都应该能够复制此问题):

# -*- coding: utf-8 -*-
uni = u'Újratárgyalja'
enc1 = uni.encode('utf-8')
enc2 = enc1.decode('latin-1').encode('utf-8')
dec3 = enc2.decode('utf-8')
dec4 = dec3.encode('latin-1').decode('utf-8')

for x in (uni, enc1, enc2, dec3, dec4):
  print repr(x), x

这是一段有趣的输出...

u'\xdajrat\xe1rgyalja' Újratárgyalja
'\xc3\x9ajrat\xc3\xa1rgyalja' Újratárgyalja
'\xc3\x83\xc2\x9ajrat\xc3\x83\xc2\xa1rgyalja' Ãjratárgyalja
u'\xc3\x9ajrat\xc3\xa1rgyalja' Ãjratárgyalja
u'\xdajrat\xe1rgyalja' Újratárgyalja

Ã开头的奇怪字符串出现为enc2,即两个utf-8编码夹杂着一个Latin-1解码。并且你可以看到,这可以通过恰好相反的操作序列来撤消:先解码为utf-8,再重新编码为Latin-1,最后再次解码为utf-8 - 原始字符串就回来了(耶!)。
我相信,Latin-1(又称ISO-8859-1)和UTF-8的正常往返属性应该保证此序列将起作用(很抱歉,目前没有C#语言的环境尝试,但我期望编码/解码序列不应该依赖于使用的特定编程语言)。

聪明。答案被接受了。但出于好奇,我尝试在Windows上使用Python 2.6.1重现您的结果。这很困难,因为复制和粘贴代码本身就会产生问题(例如,将其粘贴到记事本和IDLE编辑器中会有很大的不同!)。然后执行它会创建更多的问题(如果在IDLE之外,则必须仅打印repr(x))。[我知道,我知道,获取一个真正的操作系统等等。] - John Y

1

当你写道“MySQL .Net连接器以这种方式下载它们”时,很有可能意味着MySQL .Net连接器认为它在与MySQL通信时使用的是Latin-1编码,而MySQL则认为这次对话是使用UTF-8编码。

也有可能是列被声明为Latin-1编码,但实际上包含的是UTF-8数据,如果使用MySQL的文本处理函数、对该列进行排序(ORDER BY),或者其他那些涉及到文本“有意义”的情况,而不仅仅是字节在传输过程中的问题,则会出现神秘的排序问题和其他错误。

无论哪种情况,你应该尽量修复潜在问题,这样至少可以避免给维护系统的人带来一场完全的头疼。


该列被声明为UTF-8,并且存储在其中的数据也是UTF-8,但由于某些神秘的原因,PHP的PDO扩展对其进行了两次编码。 - RoliSoft
你之前从未提到过 PHP,所以 MySQL 数据库中的数据是否真的损坏了? - tialaramex
我在第一句话中就提到了,它以那种方式存储在MySQL数据库中。然而,我没有提到PHP的PDO扩展是以这种方式存储它们的,因为最初的问题是如何解码在C#中被UTF-8编码两次的字符串。 - RoliSoft
好的,在这种情况下,你肯定应该在数据库中修复它,因为我在第二段中提到的所有原因。PHP + MySQL很容易被错误地配置,我自己也犯过这个错误,但幸运的是我很快就发现了。你可以在MySQL内部进行相同的解码/编码操作,但需要小心(首先创建备份)。 - tialaramex

-1

你可以尝试使用

SELECT CONVERT(`your_column` USING ascii)
FROM `your_table`

在MySQL查询层面上。虽然这只是一次猜测。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接