hello, unicode surrogate

版权

本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/123441548.

Unicode

unicode 给地球上每一个字符(character) 分配一个整数, 这些整数的范围称为codespace(编码空间; [参考1] 2.4 Code Points and Characters). 空间里的每个整数称为一个code point(编码点).

在上世纪unicode 总共容纳了几万个字符, 当时用2个字节(uint16, 0~65535) 可以表示每个code point. 后来字符越来越多容纳不下, 为保持兼容, 增加一个uint16(即用一对uint16) 来表示超出范围的新字符. 这种编码形式称为UTF-16, 其中每个uint16(即2个字节) 称为其code unit(编码单位; [参考1] 2.5 Encoding Forms).

unicode 还有UTF-32UTF-8 两种编码形式. 前者固定用4个字节(定长) 来表示一个字符, 其编码单位(code unit)=4字节; 后者用1到4个字节(变长) 来表示一个字符, 其code unit=1字节.

unicode 目前(14.0.0) code point 的范围是0~0x10FFFF(1,114,111, 21比特). 每64K(即65536, uint16) 划分为一个plane(平面; [参考1] 2.8 Unicode Allocation), 0~16共有17个平面(65536*17=1,114,112). 其中平面0(即上世纪的code point 范围, 0x0000~0xFFFF) 称为BMP(Basic Multilingual Plane, 基本多语言平面), 大部分人大部分时候使用的字符应该都不会超出该平面.

BMP 里0xD800~0xDFFF code point 称为surrogate code([参考1] 2.9 Details of Allocation Figure 2-14. Allocation on the BMP), UTF-16 用来编码BMP 之外所有平面的字符(这些称为supplementary character, 辅助字符). 规则见[参考2] 3.9 Unicode Encoding Forms Table 3-5. UTF-16 Bit Distribution(另, [参考3]):

  • BMP 内的字符其编码 = code point 值
  • BMP 外的字符其code point = 000uuuuu xxxxxxxx xxxxxxxx(21比特有内容), 则其编码 = 110110wwwwxxxxxx 110111xxxxxxxxxx, 其中wwww = uuuuu - 1

举例, 平面2 的第一个字符U+20000 “𠀀” 字:
code point:
000uuuuu xxxxxxxx xxxxxxxx
00000010 00000000 00000000
wwww = uuuuu - 1 = 00010 - 1 = 0001
编码:
110110wwwwxxxxxx 110111xxxxxxxxxx
1101100001000000 1101110000000000
55360 56320
0xD840 0xDC00 - 均是surrogate code(0xD800~0xDFFF).

如上, 对于1个辅助字符, UTF-16 编码成2个surrogate codes - 称为surrogate pair(代理对, [参考1] 2.5 Encoding Forms - UTF-16). 代理对的开始code unit 也称为high/leading surrogate, 结束code unit 也称为low/trailing surrogate.

一般仅UTF-16 需考虑surrogate 问题, 例如遇到孤儿surrogate code(不成对的), 可以替换为unicode 替换字符(0xFFFD) 或者丢弃等. 注意, 单个surrogate code 仅仅是一个code point 而已, 没有对应的unicode 字符.

Java

Java 设计于上世纪, 所以String 是以UTF-16 code unit 为单位, 即String = 一组char, char = 2个字节([参考4]), char 范围0~65535.

public final class String implements ...... {
    private final char value[];
    ......
}

通常的操作(包括下标) 都是针对这个char[], 当需要特殊处理code point 时有额外的方法.

        String s1 = "A𠀀B", s2 = "A\uD840\uDC00B";
        // len=4, equal
        System.out.printf("len=%d, %s\n", s1.length(),
                s1.equals(s2) ? "equal" : "not equal");

        char c = s1.charAt(1);
        System.out.println((int) c); // 55360

        int i = s1.codePointAt(1), j = s1.codePointAt(2);
        System.out.printf("0x%H, %d\n", i, j); // 0x20000, 56320

        String subs = s1.substring(2);
        // "?B" len=2, [0] isLowSurrogate: true
        System.out.printf("\"%s\" len=%d, [0] isLowSurrogate: %b\n", subs, subs.length(),
                Character.isLowSurrogate(subs.charAt(0)));

Go

Go 比较独特: 源代码只使用UTF-8; string 就是一组byte, 内部并没有预先拆成一个个rune 而是在运行时动态拆开.

	s := "A\U00020000B"
	// len=6
	fmt.Printf("len=%d\n", len(s))
	// [0] 0x41 [1] 0x20000 [5] 0x42
	for i, c := range s {
		fmt.Printf("[%d] 0x%X ", i, c)
	}
	fmt.Println()

可使用unicode/utf16 包来处理UTF-16.

Windows C

#include <windows.h>
#include <stdio.h>

int main()
{
    WCHAR * s = L"A𠀀B";
    wprintf(L"len=%d, sizeof=%d\n", wcslen(s), sizeof s); // len=4, sizeof=8
    s++;
    wprintf(L"high surrogate=%d\n", (int)*s); // high surrogate=55360
    s++;
    wprintf(L"low surrogate=%d\n", (int)*s); // low surrogate=56320
    return 0;
}

Windows 也是类似Java, 使用UTF-16(wide character) 来支持unicode([参考5]).

相关文章

mysql | Incorrect string value: ‘\xE7) \xE5\xA4\xB1…‘

参考

[1] https://www.unicode.org/versions/Unicode14.0.0/ch02.pdf
General Structure

[2] https://www.unicode.org/versions/Unicode14.0.0/ch03.pdf
Conformance

[3] https://unicode.org/faq/utf_bom.html#utf16-3
Q: What’s the algorithm to convert from UTF-16 to character codes?

[4] https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.10.4
Java Language Specification
Chapter 3. Lexical Structure

[5] https://docs.microsoft.com/en-us/windows/win32/intl/unicode

[6] https://docs.microsoft.com/zh-cn/windows/win32/learnwin32/working-with-strings
操作字符串

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值