将___编码转换为UTF-8——是否有一个万能解决方案?

4
我已经在网上、SO、PHP文档等地寻找了解决方案。
这似乎是一个没有标准解决方案的荒谬问题。如果您获得了一个未知的字符集,并且它包含奇怪的字符(比如英语引号),是否有一种标准方法将它们转换为UTF-8?
我看到了很多混乱的解决方案,使用大量函数和检查,但它们都不一定能正常工作。
有人想出了自己的函数或一种始终有效的解决方案吗?
编辑
许多人回答说“无法解决”或类似的话。我现在明白了,但除了非常有限的utf8_encode之外,没有人给出任何真正有效的解决方案。有哪些方法可以处理这个问题?什么是最佳方法?

维基百科上有一个很好的解释,关于无法猜测乱码文本的问题。http://en.wikipedia.org/wiki/Replacement_character#Replacement_character - deceze
4个回答

11

没有人应该不知道一个字符串属于哪种字符集。通过使用嗅探功能猜测字符集是不可靠的(虽然在大多数情况下,在西方世界中,它通常是ISO-8859-1和UTF-8之间的混淆)。

但为什么你必须处理未知的字符集?这没有普遍的解决方案,因为一般问题本来就不应该存在。每个网页和数据源都可以和应该有一个字符集定义,如果没有,就应该请求该资源的管理员添加一个字符集定义。

(不想显摆,但这确实是处理这个问题的唯一方法。)


2
RSS订阅是需要进行此操作的常见示例。人们上传文件或从各种不同的编辑器复制和粘贴,使用不同的字符集在他们的电脑上。 - Kerry Jones
5
将文本复制粘贴到表单中并不是问题,因为如果文本没有转换成Unicode格式放在剪贴板上,浏览器也不知道如何显示它。当文本被放置在剪贴板上时,它会被转换成Unicode格式,浏览器知道将文本转换成发送所需的任何编码。由于XML声明,RSS订阅源也不应该有问题,但如果缺少XML声明,则它可能在许多其他地方失败,除非编码为UTF-8或UTF-16。 - Michael Madsen
1
@Michael,是的,我对剪贴板也有同样的想法。然而,有时候可能会出现混乱的情况,因为内容来自编码错误的网站、不同编码的外部嵌入网站等等。尽管如此,复制+粘贴可能并不像我最初想象的那么严重。 - Pekka
我不能依赖其他RSS源的管理员来维护他们的源,这样做会有很大的风险。虽然我之前不知道剪贴板的问题,但我仍然希望有一个完美的解决方案。 - Kerry Jones
我会默认这是英文,并将非UTF-8字符简单地转换为UTF-8。这是目前我看到的最好的选择。 - Kerry Jones
显示剩余6条评论

9
你看到了许多复杂的解决方案是因为从定义上来说,这个问题是无法解决的。编码文本字符串的过程是非确定性的。可以构造不同的文本和编码组合,得到相同的字节流。因此,严格逻辑上讲,无法从字节流中确定编码、字符集和文本。
实际上,使用启发式方法可以获得足够接近的结果,因为在野外遇到的编码集是有限的,通过足够大的样本,程序可以确定最可能的编码。结果是否足够好取决于应用程序。
我想评论一下用户生成的数据的问题。从网页发布的所有数据都有一个已知的编码(POST带有开发人员为页面定义的编码)。如果用户将文本粘贴到表单字段中,浏览器将根据源数据的编码(由操作系统知道)和页面编码来解释文本,并在必要时进行转码。在服务器上检测编码已经太迟了,因为浏览器可能已经根据假定的编码修改了字节流。
例如,如果我在德语键盘上键入字母Ä并将其发布在UTF-8编码的页面上,则会发送2个字节(xC3 x84)到服务器。这是一个有效的EBCDIC字符串,表示字母C和d。这也是一个有效的ANSI字符串,表示2个字符Ã和„。然而,无论我尝试什么,都不可能将一个ANSI编码的字符串粘贴到浏览器表单中,并期望它被解释为UTF-8——因为操作系统知道我正在粘贴ANSI(我从Textpad复制了文本,其中创建了一个ANSI编码的文本文件),并将其转码为UTF-8,结果是字节流xC3 x83 xE2 x80 x9E。
我的观点是,如果用户设法发布垃圾数据,则可以认为这是因为在将其粘贴到浏览器表单时已经是垃圾数据,因为客户端没有正确支持字符集、编码等。由于字符编码是非确定性的,所以不能指望存在一种简单的方法从这种情况中发现问题。
不幸的是,对于上传的文件,问题仍然存在。我看到的唯一可靠的解决方案是向用户显示文件的一部分,并询问是否正确解释,然后循环使用一堆不同的编码,直到达到正确的结果。

或者我们可以开发一种启发式方法来查看各种语言中某些字符的出现情况。比如我上传了一个包含两个字节xC3 x84的文本文件。除了文件中的两个字节外,没有其他信息。这种方法可以发现Ä这个字母在德语文本中相当常见,但是Ã和„这两个字母在任何语言中都不常见,从而确定我的文件的编码确实是UTF-8。这大致是这种启发式方法所要处理的复杂程度,它所能使用的统计和语言事实越多,其结果就越可靠。


谢谢您的帖子 - 我已经更新了我的问题,我觉得您可能有最好的答案。您给了我一个可能实现的想法,是否有任何脚本或函数可以处理它? - Kerry Jones
没有通用的解决方案。这取决于您的情况。例如,如果可以通过限制编码或语言数量等方式以某种形式减少问题,则可能存在解决方案。例如,请查看此帖子:https://dev59.com/S3RA5IYBdhLWcg3w2xsI。每个建议的解决方案似乎都有其局限性。 - cdonner
看起来我得进行一些查找和替换,将范围之外的内容限制在英语语言范围内。 - Kerry Jones

1
Pekka 关于不可靠性的观点是正确的,但如果您需要一种解决方案,并且愿意承担风险,并且具有可用的 mbstring 库,则此代码片段应该可以工作:
function forceToUtf8($string) {
    if (!mb_check_encoding($string)) {
        return false;
    }
    return mb_convert_encoding($string, 'UTF-8', mb_detect_encoding($string));
} 

我认为这可能是许多人的好解决方案,但将return false更改为return $string对我没有起作用。 - Kerry Jones
3
mb_detect_encoding只能识别少量的编码格式,包括UTF-8、UTF-7、ASCII和一些日语编码格式。它无法识别大部分其他编码格式。 - Michael Madsen

0

是的,我尝试过了,如果失败它也会返回一个空字符串。 - Kerry Jones
根据手册,utf8encode 仅适用于 ISO-8859-1 字符串,因此对于未知编码的情况并不真正有帮助。 - Pekka

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