字符集如何影响存储/显示的说明

Exp*_*lls 5 mysql character-encoding

我发现我还不明白这一点,这让我很生气,但也许一些解释会有所帮助。这是一个由两部分组成的问题,但希望这两部分都很小并且直接相关:

\n\n

展示

\n\n

我们最近遇到一个问题,内容是将U+00a0(不间断空格)字符插入具有字符集的数据库列中latin1SELECT只需在列中打印出“\xc3\x82”即可。我不确定这是选择的产物还是展示的产物,但我相信是前者。 SELECT BINARY col相反,它会打印出“\xc2\xa0”,因为我的 shell 有$LANG = en_US.utf8.

\n\n

一个更明显的例子是“\xc3\xa2\xe2\x80\x9e\xc2\xa2”与“\xe2\x84\xa2”

\n\n

使用SELECT CONVERT(col USING utf8)still 打印出“\xc3\x82”和“\xc3\xa2\xe2\x80\x9e\xc2\xa2”——我不一定希望它有不同的做法,但问题源于何处?是存储时出现的问题吗?有没有办法让 UTF8 显示从数据库中出来,而不是依赖 UI 来正确显示(如果这有意义的话?)

\n\n

贮存

\n\n

为了尝试自己重现此问题,我执行了以下操作:

\n\n
CREATE TABLE chrs (\n    lat varchar(255) charset latin1,\n    utf varchar(255) charset utf8\n);\nINSERT INTO chrs VALUES (\'\xe2\x84\xa2\', \'\xe2\x84\xa2\');\nINSERT INTO chrs VALUES (\'\xc2\xa0\', \'\xc2\xa0\'); -- U+00a0\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,这会导致:

\n\n
> SELECT * FROM chrs;\n+------+------+\n| lat  | utf  |\n+------+------+\n| \xe2\x84\xa2    | \xe2\x84\xa2    |\n| \xc2\xa0    | \xc2\xa0    |\n+------+------+\n
Run Code Online (Sandbox Code Playgroud)\n\n

我希望lat显示“\xc3\x82”和“\xc3\xa2\xe2\x80\x9e\xc2\xa2”,所以显然有一些我不明白的东西。

\n\n

更重要的是:

\n\n
 > SELECT BINARY lat, BINARY utf FROM chrs;\n+------------+------------+\n| BINARY lat | BINARY utf |\n+------------+------------+\n| \xef\xbf\xbd           | \xe2\x84\xa2          |\n| \xef\xbf\xbd           | \xc2\xa0          |\n+------------+------------+\n
Run Code Online (Sandbox Code Playgroud)\n\n

这表明这些值未正确存储(?)到lat.

\n\n

我注意到那SELECT @@character_set_clientutf8,所以我将其更改为latin1并再次插入空格,但这会产生

\n\n
| \xc3\x82\xc2\xa0   | \xc3\x82\xc2\xa0   |\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于列。 SELECT BINARY lat正确显示空格,但SELECT binary utf8仍打印出“\xc3\x82”。我希望该专栏能够utf8正确地工作。

\n\n

总结一下:

\n\n
    \n
  • 当您插入字符时,MySQL 实际上对它们做了什么?它取决于列字符集、客户端集、两者还是其他什么?
  • \n
  • 由于上述不匹配,是否有可能在插入时搞乱数据?或者是否总是可以恢复最初插入的数据?
  • \n
  • charset列上的 实际在存储/显示方面有何作用
  • \n
\n

min*_*ret 3

简而言之,您的数据库似乎没问题,除非您通过将 [@@character_set_client] 从 [utf8] 更改为 [latin1] 来明确告诉它表现奇怪。否则,我认为您会看到使用 UTF-8 与 Windows-1252 的软件组件之间在其他地方存在分歧的影响。

\n\n

我们如何理解正在发生的事情?

\n\n

首先,我们回想一下,在 MySQL 中 latin1 实际上意味着 Windows-1252,这种编码与“Latin-1”本身略有不同,也称为 ISO/IEC 8859-1。

\n\n

现在让我们考虑以下有关商标符号和不间断空格的数据:

\n\n
    \n
  • 字符:“商标标志”
  • \n
  • Unicode 点:U+2122
  • \n
  • UTF-8 十六进制字节:E2 84 A2
  • \n
  • Latin-1 (ISO 8859-1) 十六进制字节:此编码中没有该字符的代码
  • \n
  • Windows 1252 十六进制字节:8D

  • \n
  • 字符:“不间断空格”

  • \n
  • Unicode 点:U+00A0
  • \n
  • UTF-8 十六进制字节:C2 A0
  • \n
  • Latin-1 (ISO 8859-1) 十六进制字节:A0
  • \n
  • Windows 1252 十六进制字节:A0
  • \n
\n\n

出现问题的各种方式:

\n\n
    \n
  • 将商标符号 UTF-8 十六进制字节解释为 Windows 1252 字节而产生的字符: \xc3\xa2 \xe2\x80\x9e \xc2\xa2\n
      \n
    • “带扬抑符的拉丁小写字母 a”、“双低 9 引号”、“分号”
    • \n
    • 注意:Latin-1 和 Unicode 根本没有对 Windows-1252 定义为“双低 9 引号”的十六进制字节 84 进行解码。Unicode 在远离该处的代码点 U+201E 处对“双低 9 引号”进行编码。
    • \n
  • \n
  • 将不间断空格 UTF-8 十六进制字节解释为 Windows 1252 字节而产生的字符: \xc3\x82 [不间断空格]\n
      \n
    • “带抑扬符的拉丁大写字母 a”、“不间断空格”
    • \n
  • \n
  • 将商标符号 Windows-1252 十六进制字节解释为 UTF-8 字节所产生的字符:[无字符:显示平台的缺失字符标记,通常是问号符号的变体]
  • \n
\n\n

看来,当您插入时,数据库将商标符号存储在“latin1”中作为十六进制字节 8D,在“UTF-8”中存储为十六进制字节 E2 A4 A2。它将不间断空格存储在“latin1”中作为十六进制字节“A0”,在UTF-8中存储为十六进制字节C2 A0。当您以交互方式执行正常的 SELECT 操作时,“latin1”商标符号首先会转换为 Unicode 点 U+2122,然后转换为 UTF-8 十六进制字节 E2 84 A2,最终可能会被误解为 Windows-1252 字节。

\n\n

在哪里可以找到上面显示的有关字符的数据:

\n\n\n