我觉得令人愤怒的是我还不明白这一点,但也许一些解释会有所帮助。这是一个两部分的问题,但希望这两个部分都很小且直接相关:
我们最近遇到了一个问题,即内容将U+00a0
(不间断空格)字符插入到具有latin1
字符集的数据库列中。只需执行SELECT
就会在列中打印出“”。我不确定这是选择还是显示器的产品,但我相信它是前者。 SELECT BINARY col
代替打印出“”,因为我的shell有$LANG = en_US.utf8
。
一个更明显的例子是“┢“vs.”™“
使用SELECT CONVERT(col USING utf8)
仍打印出“”和“┢“ - 我不一定会指望它做的不同,但问题源于何处?这是在存储时发生的问题吗?有没有办法让UTF8显示器从数据库中取出而不是依靠UI来正确显示它(如果这有意义的话?)
为了尝试自己重现这个问题,我做了以下几点:
CREATE TABLE chrs (
lat varchar(255) charset latin1,
utf varchar(255) charset utf8
);
INSERT INTO chrs VALUES ('™', '™');
INSERT INTO chrs VALUES (' ', ' '); -- U+00a0
然而,这导致:
> SELECT * FROM chrs;
+------+------+
| lat | utf |
+------+------+
| ™ | ™ |
| | |
+------+------+
我希望lat
能显示“”和“â”,“所以显然我有些不明白。
更重要的是:
> SELECT BINARY lat, BINARY utf FROM chrs;
+------------+------------+
| BINARY lat | BINARY utf |
+------------+------------+
| � | ™ |
| � | |
+------------+------------+
这表示值未正确存储(?)到lat
。
我注意到SELECT @@character_set_client
是utf8
,因此我将其更改为latin1
并再次插入空格,但这会产生
| Â | Â |
两个列。 SELECT BINARY lat
正确显示空格,但SELECT binary utf8
仍打印出“”。我希望utf8
列可以正常使用更多。
charset
实际做是什么?答案 0 :(得分:3)
简而言之,您的数据库似乎没问题,除非您通过将[@@ character_set_client]从[utf8]更改为[latin1]来明确告诉它行为异常。否则,我认为你在使用UTF-8和Windows-1252的软件组件之间看到了分歧的影响。
我们如何理解正在发生的事情?
首先,我们回想一下in MySQL latin1 really means Windows-1252,这是一种与“Latin-1”本身略有不同的编码,也称为ISO / IEC 8859-1。
现在让我们考虑以下有关商标标志和不间断空间的数据:
Windows 1252十六进制字节:8D
字符:“不间断的空间”
出现问题的各种方法:
当您插入时,您的数据库会将商标符号存储为“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字节。
在哪里可以找到上面显示的关于字符的数据:
答案 1 :(得分:1)
如果链中的每个字符切换都支持UTF8,则该字符应存储为UTF8字段中的3个字节,其十六进制为:
E284A2
并且,在latan1字段中,为1字节,其十六进制为:
99
但是,您的客户端和连接在正确存储字符并将其显示为 as-stored 方面起着关键作用。
通过latin1
连接与latin1
客户端连接,我创建并插入了两行。更改为utf8客户端/连接并重新插入。结果如下:
从我的latin1连接中选择:
mysql> select *, hex(lat), hex(utf) from chrs;
+------+------+----------+----------------+
| lat | utf | hex(lat) | hex(utf) |
+------+------+----------+----------------+
| ™ | ™ | E284A2 | C3A2E2809EC2A2 |
| | | 20 | 20 |
| ? | ? | 99 | E284A2 |
| | | 20 | 20 |
+------+------+----------+----------------+
从我的utf8连接中选择:
mysql> select *, hex(lat), hex(utf) from chrs;
+---------+---------+----------+----------------+
| lat | utf | hex(lat) | hex(utf) |
+---------+---------+----------+----------------+
| â„¢ | â„¢ | E284A2 | C3A2E2809EC2A2 |
| | | 20 | 20 |
| ™ | ™ | 99 | E284A2 |
| | | 20 | 20 |
+---------+---------+----------+----------------+
在我看来,这里最令人困惑的行为是C3A2E2809EC2A2
以某种方式在从latin1客户端和连接中选择时正确呈现。但是,考虑到该字段是UTF8,MySQL毫无疑问会将每组3个字节转换为相应的latin1字节进行传输,从而通过连接发送E284A2
。而我的终端恰好将这三个字节解释为UTF8。 (但是,这有点推测。我不完全确定在哪里发生“无意中正确”的转换。)
当然,MySQL以类似但相反的方式处理拉丁语99
。