字符集和数据库

时间:2015-09-22 09:55:11

标签: php encoding character-encoding

我的老板喜欢使用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。然后打印好了。但是为什么!?

1 个答案:

答案 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

您需要修复存储在数据库中的数据:

  1. 确切地确定现有数据的实际编码方式。如上所述,某些值可能是三重编码的,但其他值(可能早于应用程序代码的特定更改;或者从不同源插入/更新的值)可能以某种其他方式编码。我发现latin1对此非常有用。

  2. 更正当前不正确的值的编码:例如SELECT HEX(myColumn) FROM myTable WHERE ... - 如果整个列被错误编码,您可以使用UPDATE myTable SET myColumn = BINARY CONVERT(myColumn USING latin1) WHERE ...将其更改为二进制字符串类型,然后返回到正确编码的字符串。请注意增加编码长度的转换,因为结果可能会溢出现有的列大小。