? unicode

smile unicode 的utf8编码是F0 9F 98 8A(十六进制表示)。

c代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char * argv[]) {
    char * smile = "?";
    printf("%s\n", smile);
    
    printf("len:%ld\n", strlen(smile));
    
    for (int i=0; i<strlen(smile); i++) {
        printf("%X\t",smile[i]);
    }
    
    puts("");
    return EXIT_SUCCESS;
}

输出结果:

?
len:4
FFFFFFF0    FFFFFF9F    FFFFFF98    FFFFFF8A

问题:

  1. 为啥高位都是F呢? 打印char应该用%hhX,用%X隐含类型提升,补了高位的1。

  2. 每个字节都只用了最后两位,一个字节就能装下,为什么要分到四个字节呢? 0xF0 就是一个字节--八位。

  3. mysql的utf8只有三字节,『?』需要四个,如果不用utf8mb4就会丢失标签显示乱码。那么这个长度检查是在哪个环节处理的呢?一定要检查每个字符的合法性吗?直接存码流行吗?

阅读 3.9k
5 个回答

因为smile[i]被自动提升为int型了。因为你的那些字节最高位都是1。所以提升时补1,打印的高位都是F。用%02X就可以了。

F09F988A 转成二进制:11110000 10011111 10011000 10001010

红色的是utf8的固定格式

每个字节的第一位数值是1,printf先把单字节转成long(4字节),自动把左边多出来的字节用1填满。

所以就打印出来 4字节的FFFFFFxx

2

%x的"一位"是十六进制的1个数字,也就是4bit / 半字节。这个符号的utf8表示真的就是4字节。不信你可以printf("%0x",* (unsigned int*)smile);

1

c里变元函数的参数最短是int. 把char赋值给int时,左边补的位由char的最高位决定. f0 9f 98 8a四个字节的高位都是1, 所以你会看到很多F (1111).

另外 %x是给unsigned int用的, 打印1字节char可以用%hhx来忽略更高的位

  1. printf %什么与后面的参数应当完全匹配,否则行为未定义。以十六进制输出内存中的值应该 printf("%X", *(unsigned int *)smile);

  2. 1个byte 8bit,用2个十六进制字符表示。

  3. 与Mysql字符集的collation算法有关。

MySQL 那个字符集的问题,我至今还没搞懂它为什么需要再加一个莫名其妙的 utf8mb4 「字符集」。也从没有听说别的使用 UTF-8 的软件需要对 U+10000 之后的字符进行特殊处理的。

只有使用 UCS-2,即两字节来表示字符的系统需要对这些字符特殊处理。比如 Windows、Java,好像 JavaScript 也是,内部使用的是 UTF-16,但是一些 API 里会把它当成 UCS-2 来处理,即认为编码是两字节定长的,导致对字符数量计算有误。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏