MySQL CHAR_LENGTH(str) 与不同的字符集引入器一起使用时,有时会为相同的字符串文字产生不同的输出
MySQL CHAR_LENGTH(str) sometimes produces different outputs for the same String literal when used with different character set introducers
这很奇怪。根据 MySQL 开发者网站 -
Returns the length of the string str, measured in characters. A multibyte character counts as a single character. This means that for a string containing five 2-byte characters, LENGTH() returns 10, whereas CHAR_LENGTH() returns 5.
很明显的意思是CHAR_LENGTH(str)的输出与字符集无关
现在如 -
Character String Literal Character Set and Collation
我可以使用 introducer 来设置字符串的字符集,如 -
SELECT 'abc';
SELECT _latin1'abc';
SELECT _binary'abc';
SELECT _utf8mb4'abc' COLLATE utf8mb4_danish_ci;
在我的例子中,我采用字符串文字“Hello”,使用介绍符将其设置为字符集,并将其用作 MySQL CHAR_LENGTH(str) 函数的参数。但奇怪的是,当使用不同的字符集时,它有时会产生不同的输出。示例 -
SELECT CHAR_LENGTH(_utf8mb4"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 5 |
+------------------+
SELECT CHAR_LENGTH(_latin1"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 5 |
+------------------+
SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 3 |
+------------------+
在这里,“你好”有 5 个字符。字符集 _utf8mb4 和 _latin1 正确显示字符数。但是 CHARACTER SET _ucs2 奇怪地将数字 0f 字符显示为 3.
这是怎么回事?
那是因为显示的语言,这个词需要3个字符。
扩展我的回答。
字符转换在最好的情况下是困难的,应不惜一切代价避免。
首先MySQL尝试将 $Byte characte4r 转换为 ucs2 具有的 2 Byte 字符。
生成的字节随后用于显示字符,即您在代码段末尾看到的内容。
所以在字符集的转换中,你没有像计算机科学字节中到处都有的字母,它的表示形式可以说是 H,但是另一个字符集中的字节可以有另一种表示形式。此外,您始终需要一些规则,如果字节数不同,如何将一种字符集转换为另一种字符集。
所以我还扩展了示例,以向您展示确定性转换实际上发生了,如果您查看字节或二进制表示,您会发现所使用的具体算法。
SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
| character_length |
| ---------------: |
| 3 |
SELECT _ucs2"Hello";
| 䡥汬 |
| :------ |
| H敬汯 |
SELECT _ucs2"Hel";
| 䡥 |
| :--- |
| H敬 |
SELECT _ucs2"Hell";
| 䡥汬 |
| :----- |
| 䡥汬 |
SELECT _ucs2"Hellos";
| 䡥汬潳 |
| :-------- |
| 䡥汬潳 |
db<>fiddle here
看起来像一个错误:
SELECT HEX(_ucs2"HELLO"), HEX(CONVERT("HELLO" USING ucs2));
+-------------------+----------------------------------+
| HEX(_ucs2"HELLO") | HEX(CONVERT("HELLO" USING ucs2)) |
+-------------------+----------------------------------+
| 0048454C4C4F | 00480045004C004C004F |
+-------------------+----------------------------------+
看来_ucs2
介绍人只申请了第一个角色。
我创建了错误 https://bugs.mysql.com/bug.php?id=105394&thanks=4
来自错误报告
The _charset_name expression is formally called an introducer. It tells
the parser, “the string that follows uses character set
charset_name.” An introducer does not change the string to the
introducer character set like CONVERT() would do. It does not change the
string value, although padding may occur. The introducer is just a
signal.
这很奇怪。根据 MySQL 开发者网站 -
Returns the length of the string str, measured in characters. A multibyte character counts as a single character. This means that for a string containing five 2-byte characters, LENGTH() returns 10, whereas CHAR_LENGTH() returns 5.
很明显的意思是CHAR_LENGTH(str)的输出与字符集无关
现在如 -
Character String Literal Character Set and Collation
我可以使用 introducer 来设置字符串的字符集,如 -
SELECT 'abc';
SELECT _latin1'abc';
SELECT _binary'abc';
SELECT _utf8mb4'abc' COLLATE utf8mb4_danish_ci;
在我的例子中,我采用字符串文字“Hello”,使用介绍符将其设置为字符集,并将其用作 MySQL CHAR_LENGTH(str) 函数的参数。但奇怪的是,当使用不同的字符集时,它有时会产生不同的输出。示例 -
SELECT CHAR_LENGTH(_utf8mb4"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 5 |
+------------------+
SELECT CHAR_LENGTH(_latin1"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 5 |
+------------------+
SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
| 3 |
+------------------+
在这里,“你好”有 5 个字符。字符集 _utf8mb4 和 _latin1 正确显示字符数。但是 CHARACTER SET _ucs2 奇怪地将数字 0f 字符显示为 3.
这是怎么回事?
那是因为显示的语言,这个词需要3个字符。
扩展我的回答。
字符转换在最好的情况下是困难的,应不惜一切代价避免。
首先MySQL尝试将 $Byte characte4r 转换为 ucs2 具有的 2 Byte 字符。
生成的字节随后用于显示字符,即您在代码段末尾看到的内容。
所以在字符集的转换中,你没有像计算机科学字节中到处都有的字母,它的表示形式可以说是 H,但是另一个字符集中的字节可以有另一种表示形式。此外,您始终需要一些规则,如果字节数不同,如何将一种字符集转换为另一种字符集。
所以我还扩展了示例,以向您展示确定性转换实际上发生了,如果您查看字节或二进制表示,您会发现所使用的具体算法。
SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
| character_length | | ---------------: | | 3 |
SELECT _ucs2"Hello";
| 䡥汬 | | :------ | | H敬汯 |
SELECT _ucs2"Hel";
| 䡥 | | :--- | | H敬 |
SELECT _ucs2"Hell";
| 䡥汬 | | :----- | | 䡥汬 |
SELECT _ucs2"Hellos";
| 䡥汬潳 | | :-------- | | 䡥汬潳 |
db<>fiddle here
看起来像一个错误:
SELECT HEX(_ucs2"HELLO"), HEX(CONVERT("HELLO" USING ucs2));
+-------------------+----------------------------------+
| HEX(_ucs2"HELLO") | HEX(CONVERT("HELLO" USING ucs2)) |
+-------------------+----------------------------------+
| 0048454C4C4F | 00480045004C004C004F |
+-------------------+----------------------------------+
看来_ucs2
介绍人只申请了第一个角色。
我创建了错误 https://bugs.mysql.com/bug.php?id=105394&thanks=4
来自错误报告
The _charset_name expression is formally called an introducer. It tells the parser, “the string that follows uses character set charset_name.” An introducer does not change the string to the introducer character set like CONVERT() would do. It does not change the string value, although padding may occur. The introducer is just a signal.