V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  程序员

为什么感觉 utf8 的编码没有对上?

  •  
  •   amiwrong123 · 2022-05-17 22:06:13 +08:00 · 1763 次点击
    这是一个创建于 702 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先看第一行:

    • 0xxx xxxx(这里用 x 来代表图里的_)的 7 个 bit 都可以自由变化,所以0xxx xxxx可以有 2^7 种取值。
    • 然后计算0x00 ~ 0x7F这个范围内有多少种 取值,即 这两个数相减再加 1 ,那么为0x80,确实也是 2^7 。
    • 所以第一行,是一一对应的。

    再看第二行:

    • 110x xxxx10xx xxxx,总共有 11 个 bit 可以变化,所以110x xxxx10xx xxxx可以有 2^11 种取值。
    • 然后计算0x0080 ~ 0x07FF这个范围内有多少种 取值,即 这两个数相减再加 1 ,那么为15 * 2^7
    • 现在前者是 2^11 种取值(即2^4 * 2^716 * 2^7),后者是15 * 2^7种取值,对不上了
    • 而且现在,110x xxxx10xx xxxx多出来了一个2^7的范围,这还刚好可以把 第一行的编码给编进去,这样的话,就刚好一一对应了,但是第一行的编码不是只能用0xxx xxxx来编码吗?这不就感觉很奇怪了。
    8 条回复    2022-05-18 01:04:41 +08:00
    wudicgi
        1
    wudicgi  
       2022-05-17 22:23:11 +08:00
    可能是因为这个,有的字节是不能出现的

    https://zh.wikipedia.org/zh-cn/UTF-8

    根据这种方式可以处理更大数量的字符。原来的规范允许长达 6 字节的序列,可以覆盖到 31 位(通用字符集原来的极限)。尽管如此,2003 年 11 月 UTF-8 被 RFC 3629 重新规范,只能使用原来 Unicode 定义的区域,U+0000 到 U+10FFFF 。根据这些规范,以下字节值将无法出现在合法 UTF-8 序列中:
    编码(二进制) 编码(十六进制) 注释
    1100000x C0, C1 过长编码:双字节序列的头字节,但码点 <= 127
    1111111x FE, FF 无法达到:7 或 8 字节序列的头字节
    111110xx
    1111110x F8, F9, FA, FB, FC, FD 被 RFC 3629 规范:5 或 6 字节序列的头字节
    11110101
    1111011x F5, F6, F7 被 RFC 3629 规范:码点超过 10FFFF 的头字节
    wudicgi
        2
    wudicgi  
       2022-05-17 22:25:33 +08:00
    去掉 0xC0, 0xC1
    (2^5-2)*(2^6) = 1920 = (0x7FF-0x80)+1 就能对上了
    amiwrong123
        3
    amiwrong123  
    OP
       2022-05-17 22:40:34 +08:00
    @wudicgi #2
    好像懂了,虽然实际上 110x xxxx 和 10xx xxxx 可以把 0x0000 ~ 0x07FF 都编码进去(而不只是 0x0080 ~ 0x07FF )。
    但设计上,不允许让 属于第一行的 0x00 ~ 0x7F 这个范围 的码点,被第二行 编码进去。

    这么看的话,第三行也是一样:
    - 有 16 个自由变化 bit ,所以实际上可以把 0x0000~0xFFFF 都给编码进去的。但设计上,不允许让 0x0000~0x07FF 的码点被编码进第三行。
    az467
        4
    az467  
       2022-05-18 00:07:55 +08:00
    主要是为了禁止 overlong encoding ,没必要用 N+M 个 byte 表示 N 个 byte 就能表示的东西。
    顺便配合规范,总共就 10FFFF 个码点,不支持 4 bytes 以上的格式了。
    SoloCompany
        5
    SoloCompany  
       2022-05-18 00:17:13 +08:00 via iPad   ❤️ 1
    因为 utf8 是一个安全的编码,确保在任意地方截断后都能 recover
    举一个反例,gb 编码就不是安全的编码,在半字中间阶段会导致所有编码全部错位
    更差的例子是 big5 ,允许使用 ascii 字符作为编码的一部份,导致一些 markup 语言(包括 ini )直接出错
    3dwelcome
        6
    3dwelcome  
       2022-05-18 00:54:42 +08:00
    "因为 utf8 是一个安全的编码,确保在任意地方截断后都能 recover"
    感觉这个说法不对,就算把 110x xxxx 和 10xx xxxx 的 2^11 种取值用完,也是可以正常 recover 的。

    还是赞同一楼的说法,既然 utf8 映射 unicode 有一定冗余空间,那就没必要全部用完。保留出一些头字节和 BOM ,也是完全没问题的。
    fourthLALA
        7
    fourthLALA  
       2022-05-18 01:00:57 +08:00 via iPhone
    为啥我完全没理解。。。0x07FF − 0x0080 = 77f ,再加上 1 ,不就是 2^11 次发吗。
    fourthLALA
        8
    fourthLALA  
       2022-05-18 01:04:41 +08:00 via iPhone
    我傻逼了,请无视我
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   996 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:47 · PVG 03:47 · LAX 12:47 · JFK 15:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.