Unicode 与编程语言

编程语言中的 Unicode 因为 Unicode 可以给世界上大部分字符编码,因此大部分编程语言内部,都是使用 Unicode 来处理字符的。例如在 Java 中定义一个字符 char c = '中',这个字符实际是使用两个字节在内存中存储着他的 UTF-16 编码。所以如果将这个字符强转成整型 int i = (int) c,得到的结果 20013 (0x4E2D),就是 在 Unicode 中的 Code Point 值。
这个说法不完全准确,因为大部分编程语言定义的时候,Unicode 还没有辅助平面,所以一般都是固定的用两个字节来存储一个字符。
在有了辅助平面以后,辅助平面的字符,会被 UTF-16 编码成两个 Code Unit,需要 4 个字节来存储。而编程语言为了兼容性,不太可能将原有的 char 类型长度改为 4 个字节。所以就有可能需要用两个 char 来存储一个实际的字符。而原有的获取字符串长度的 API,实际获取到的是字符串中 Code Unit 的个数,不是实际字符的个数。获取某个位置字符的 API 也是同理,获取到的可能是一对 Code Unit 中的一个。因此需要使用编程语言提供的新的 API 或者通过额外的代码,来正确处理辅助平面的字符。
在编程语言中使用 Unicode 【Unicode 与编程语言】主要涉及以下操作:

这其中最关键的就是字符和 Code Point 之间的转换。因为这里涉及字符集的映射,如果编程语言不支持,我们就要自己外挂编码表才能实现,否则无论如何都是没办法通过枚举实现的。
而有了 Code Point 以后,根据 UTF 系列编码的规则,我们自己也可以通过代码来实现 Code Point 和字节序列的转换。当然如果编程语言内置了相关的 API,那就更方便了。
这里省略了 Code Unit 的概念,因为一般在代码中,不会有这个中间过程,直接就编码成字节序列了。
Java char 和 String 中可以使用 \uXXXX 来表示一个 Unicode 字符。String 中可以使用两个 \uXXXX 表示一个辅助平面的字符,但 char 中不行,因为一个辅助平面字符需要用两个 char 存储:

char c = '\u4E2D'; String s = "\uD840\uDC21";

String to Code Point count:
int count = "".codePointCount(0, "".length());

String/char to CodePoint:
int i1 = Character.codePointAt(new char[] {0xD840, 0xDC21}, 0); int i2 = "".codePointAt(0);

Code Point to String/char:
String s = new String(new int[] {0x20021}, 0, 1); char[] c = Character.toChars(0x20021);

String to byte array:
byte[] bytes = "".getBytes(StandardCharsets.UTF_8);

Byte array to String:
String s = new String(new byte[] {(byte) 0xF0, (byte) 0xA0, (byte) 0x80, (byte) 0xA1}, StandardCharsets.UTF_8);

Normalize:
String s = Normalizer.normalize("?", Normalizer.Form.NFD);

JavaScript String 中可以使用 \uXXXX 来表示一个 Unicode 字符。对于辅助平面的字符,可以使用 \u{XXXXXX} 来表示:
'\u{20021}'

String to Code Point count:
Array.from('').length

String to Code Point:
''.codePointAt(0).toString(16)

Code Point to String:
String.fromCodePoint(0x20021)

String to byte array:
new TextEncoder().encode('')

只支持 UTF-8,其他编码方式需要自己写代码根据 Code Point 转换。
Byte array to String:
new TextDecoder('utf-8').decode(new Uint8Array([0xF0, 0xA0, 0x80, 0xA1]))

Normalize:
'?'.normalize('NFD')

相关文章:
  • 详解字符编码与 Unicode
  • Unicode 标准化

    推荐阅读