语法解析
Unicode 转义序列会在解析代码之前得到处理。例如,
"\u0022+\u0022"
并不是一个由引号(U+0022)包围加号构成的字符串。实际上,\u0022
会在解析之前转换为"
,这回得到""+""
,也就是一个空串。
下面是利用简单的 Python 脚本对 Java 代码进行「转换」:
1 | def str_to_utf16(s, filt=None): |
输出如下:
1 | \u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0063\u006c\u0061\u0073\u0073\u0020\u0048\u0065\u006c\u006c\u006f\u0057\u006f\u0072\u006c\u0064\u0020\u007b |
上述代码在 Intellij Idea 中新建 Java 文件 HelloWorld.java
可正常编译运行。当然,除非是出于安全考虑(但还原成可读版代码并无难度),实际情况中不会有人写这样的代码。仅需关注该「特性」但可能导致一个陷阱:
会产生一个语法错误,程序会将
1 // \u00A0 is a newline\u00A0
替换为一个换行符。总之,\u
在 Java 程序解析前进行转换。
UTF-16 字符编码
基本概念
- 代码单元(Code Unit):最小的数位组合,可以表示用于处理或交换的编码文本的单位。在 Unicode 标准中,UTF-8 编码格式采用 8 位编码单元,UTF-16 编码格式采用 16 位编码单元,UTF-32 编码格式采用 32 位编码单元
- BMP 字符(BMP Character):位于 BMP(Basic Multilingual Plane,多语种基本面)代码点的 Unicode 编码字符
- BMP 代码点(BMP Code Point):在 U+0000 到 U+FFFF 范围内的 Unicode 代码点
- 补充字符(Supplementary Character):位于补充代码点的 Unicode 编码字符
- 补充代码点(Supplementary Code Point):在 U+10000 到 U+10FFFF 范围内的 Unicode 代码点
- 高代理项代码点(High-Surrogate Code Point):在 U+D800 到 U+DBFF 范围内的 Unicode 代码点
- 低代理项代码点(Low-Surrogate Code Point):在 U+DC00 到 U+DFFF 范围内的 Unicode 代码点
- 代理项对(Surrogate Pair):由两个 16 位代码单元组成,其中第一个是高代理项代码单元,第二个是低代理项代码单元
编码转换算法
1 | 0x10000 is subtracted from the code point (U), leaving a 20-bit number (U') in the hex number range 0x00000–0xFFFFF. Note for these purposes, U is defined to be no greater than 0x10FFFF. |
Java 中的 UTF-16 编码
在 Java 中,
char
类型描述了 UTF-16 编码中的一个代码单元。
一个代码单元用 16 位 / 2 字节 / 4 个 16 进制数表示,UTF-16 编码表示的字符由一个或一对代码单元表示。用一对代码单元表示的码点范围从 U+10000 到 U+10FFFF,高位代理项码点范围从 U+D800 到 U+DBFF,低代理项码点范围从 U+DC00 到 U+DFFF。
下面列举几个常用的代码单元 / 字符遍历相关的操作:
作用 | 代码 | ||
---|---|---|---|
代码单元数量 |
|
||
第 n 个代码单元 |
|
||
码点数量 |
|
||
第 i 个码点 |
|
||
顺序遍历每个代码单元 |
|
||
逆序遍历每个代码单元 |
|
||
字符串转为码点数组 |
|
||
码点数组转为字符串 |
|
完整测试代码:
1 | public class Test { |