我的老板喜欢使用n-dashes。它们总是会导致编码问题而我无法解决原因。
我将TEXT字段存储在charset:utf8_general_ci
下的数据库中。
我的网页<head>
下面有以下标记:
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
我使用以下设置从数据库中提取信息:
mysql_set_charset('UTF8',$connection);
(我知道MYSQL已被折旧)
但是当我从数据库中获取信息时,我最终会得到:
– Europe
如果我使用此字符串并通过utf8_decode
运行它,我会得到:
��? Europe
我甚至试过用utf8_encode运行它,我得到了这个:
âÃâ¬Ãâ Europe
有人可以向我解释为什么会这样吗?我不明白我甚至通过mb_detect_encoding
运行字符串,它说字符串是utf8。那么为什么不能正确打印呢?
解决方案(或者不是真正的解决方案,因为它会破坏网站的其余部分)是删除mysql_set_encoding
行,并使用utf8_decode
。然后打印好了。但是为什么!?
答案 0 :(得分:1)
你必须记住,计算机处理所有形式的数据只不过是1和0的序列。为了将那些1和0转化为有意义的东西,计算机必须以某种方式告知应该如何解释这些位。
当涉及到文本字符串时,有关其位解释的此类信息称为字符编码。例如,为简洁起见,我将用十六进制符号表示为111000101000000010010011
的位序列0xe28093
在UTF-8字符编码下被解释为您老板非常喜欢的U+2013 (EN-DASH);然而,相同的比特序列可能意味着在不同编码下的任何东西:实际上,在ISO-8859-1编码下(例如),它表示三个字符的序列:U+00E2 (LATIN SMALL LETTER A WITH CIRCUMFLEX),U+0080 (<control>)和U+0093 (SET TRANSMIT STATE)
不幸的是,在他们的无限智慧中,PHP的开发人员决定不来跟踪存储字符串变量的编码 - 这是由应用程序开发人员完成的。更糟糕的是,许多PHP函数对变量的编码做出了任意的假设,他们很乐意继续操纵你的位而不考虑后果。
所以,当你在一个字符串上调用utf8_decode
时:它会占用你提供的任何数据,找出它们碰巧在UTF-8中表示的字符,然后返回ISO-8859中编码的相同字符-1。完全有可能提出一个输入序列,当传递给这个函数时,它产生绝对任何给定的结果;实际上,如果你提供输入0xc3a2c280c293
(恰好是上面提到的三个字符的UTF-8编码),它将产生0xe28093
的结果 - UTF-8编码的“冲破“!
这样的双重编码(即UTF-8编码,被视为ISO-8859-1并转码为UTF-8)似乎是您在不致电时从MySQL检索的内容{ {1}}(在这种情况下,MySQL将结果转码为客户端在连接时指定的任何字符集 - 标准驱动程序使用mysql_set_charset
,除非您覆盖其默认配置)。为了使MySQL转码到latin1
生成这种双重编码的UTF-8的结果,实际存储在列中的值必须是三重编码(即UTF-8编码) ,被视为ISO-8859-1,转码为UTF-8,然后再次被视为latin1
!
您需要修复存储在数据库中的数据:
确切地确定现有数据的实际编码方式。如上所述,某些值可能是三重编码的,但其他值(可能早于应用程序代码的特定更改;或者从不同源插入/更新的值)可能以某种其他方式编码。我发现latin1
对此非常有用。
更正当前不正确的值的编码:例如SELECT HEX(myColumn) FROM myTable WHERE ...
- 如果整个列被错误编码,您可以使用UPDATE myTable SET myColumn = BINARY CONVERT(myColumn USING latin1) WHERE ...
将其更改为二进制字符串类型,然后返回到正确编码的字符串。请注意增加编码长度的转换,因为结果可能会溢出现有的列大小。