这篇博客用于记录学习字符编码过程中得到的结论,不做过多的理论讲述。
参考资料:字符集与编码(五)
1. char 类型
在Java中,char类型存放一个用UTF-16编码代码单元(16位)表示的字符,用于表示单个字符,通常用来表示字符常量。例如:‘A‘是编码为65所对应的字符常量。
与"A"不同,"A"是一个包含字符A的字符串。Unicode代码点可以表示为十六进制值,其范围从\u0000到\uFFFF。例如:\u2122表示注册符号,\u03C0表示希腊字母π。
Java 中的 char 使用了 16 位,它可以放一个英文字符,一个中文字符,因为这些字符都在 BMP 中,如果一个字符来自增补平面,那么它将无法放入 char 中。
2. 增补字符的转义表示
另外我们可以以转义的代理对的方式表示增补字符,这样可以避免字库方面的问题。
当然了,任何字符都可以用转义的方式表示,而不仅仅限于增补字符。如果你没有安装输入法,可以简单使用 \u4F60 来表示“你”这个字符。
注:如果担心传输中出现乱码,可使用转义的表示,这时只含有 ASCII 字符,不过这种表示法效率很低。比如我们要用“\, u, 4, F, 6, 0”整整 6 个 ASCII 字符才能表示“你”这个汉字,也即要用 6 字节才能表示。而直接表示的话,用 GBK 只要两字节,而用 UTF-8 也不过是三字节。
例如 U+1D11E,写成 UTF-16 的代理对是(D8 34 DD 1E)
Java中的转义表示始终是以 \u 后面接4个16进制数字为界的(其实就是UTF-16的代码单元),你不能简单像码点那样写成 \u1D11E,这种写法相当于 "\u1D11" + "E",即只对前面4位 1D11 做转义,后面的E当成普通字母。如果要转义的字符码点超过 U+FFFF,我们需要两个一对的转义 \uD834\uDD1E 来表示,从这里我们也可以看到,所谓的转义表示其实就是UTF-16编码。
3. Java 中的 string.length 究竟指什么
如果你阅读一下 java 中 String 类中的 length 方法的说明,就会注意到以下文字:
Returns the length of this string. The length is equal to the number of Unicode code units in the string.
返回字符串的长度,这一长度等于字符串中的 Unicode 代码单元的数目。
我们知道 Java 语言里 String 在内存中以是 UTF-16 方式编码的,所以长度即是 UTF-16 的代码单元数目。
UTF-16 保存 BMP 中的字符时,使用了两个字节,也即一个代码单元。这就意味着,Java中所有的BMP字符长度都是1,无论是中文还是英文。这一点我想大家都没有疑问,代码示例如下:
@Test public void testStringLength() { String str = "hello你好"; assertThat(str.length()).isEqualTo(7); }
不出所料,“hello你好” 有5个英文字符和2个中文字符,所以它的长度就是7。但我们知道,UTF-16 同样可以表示增补平面中的字符,而且用了四字节来表示,也即两个代码单元,那么一个增补平面中的字符,它的长度就是2。
4. string.codePointCount, string.codePointAt
根据上文描述,string.length 返回的是字符串中代码单元的个数,而不是实际的字符数量。Java 中获取字符数量可以使用 codePointCount,查看 API 说明如下:
Returns the number of Unicode code points in the specified text range of this String. The text range begins at the specified beginIndex and extends to the char at index endIndex - 1. Thus the length (in chars) of the text range is endIndex-beginIndex. Unpaired surrogates within the text range count as one code point each.
返回该字符串索引范围内的Unicode代码点的数量。索引范围为 [ beginIndex, endIndex - 1] 闭区间,长度(以char为单位)就是endIndex-beginIndex。如果索引范围内有代理区的代码点,但不是以代理对的方式出现,则该码点认为是一个普通的Unicode代码点。
string.codePointAt API说明如下:
If the char value specified at the given index is in the high-surrogate range, the following index is less than the length of this String, and the char value at the following index is in the low-surrogate range, then the supplementary code point corresponding to this surrogate pair is returned. Otherwise, the char value at the given index is returned.
如果index处的char值在高代理区,紧随其后的的index小于String.length,并且紧随其后的char值在低代理区,则返回此代理对表示的增补字符的码点。否则,返回index处的char值。
//UTF-16编码,使用两个代码单元表示增补字符,Unicode码点为U+1D11E。这里data表示两个码点构成的字符串 String data = "\uD834\uDD1E\uD834\uDD1E"; System.out.println(data.length());//输出4,可以看出string.length表示代码单元的个数 System.out.println(data.codePointAt(0)); //0x1D11E,输出Unicode代码点的值 System.out.println(data.codePointAt(1)); //0xDD1E,输出index处的char值 System.out.println(data.codePointAt(2)); //0x1D11E,输出Unicode代码点的值 System.out.println(data.codePointAt(3)); //0xDD1E,输出index处的char值 System.out.println(data.codePointCount(0, data.length()));// 2,表示Unicode代码点的数量 System.out.println(data.codePointCount(0, data.length()-1)); //2 System.out.println(data.charAt(0)); // 0xD834 System.out.println(data.charAt(1)); // 0xDD1E,可以看出charAt表示代码单元的值
原文地址:https://www.cnblogs.com/lingxue3769/p/12218530.html