char类型
表示单个字符,通常表示字符常量。'A’是char类,"A"是字符串;
转义序列;‘\u2122’是™,’\u’相当于是使用Unicode的转移序列符,后面的序列相当于Unicode编号;
所有的转义序列都可以出现在’'或""中;
转义序列 \u 可以出现在引号之外,其他的都不可以;
注意:
-
Unicode转义序列会在解析代码之前得到处理,比如:“\u0022 + \u0022"并不是字符串(”+“,而是一个空串,因为在解析代码之前,就已经是”“+”",空串加空串了;
-
一定要小心注释中\u造成的歧义!比如:
// 我们使用了\u000A
会产生一个语法错误,因为直接产生了一个回车;
// 文件在c:\user
也会产生一个错误,因为\u后面没有跟着4个16进制数(应该要用\转义一下)
Unicode和char类型
参考链接:
Unicode查询链接:https://symbl.cc/cn/unicode/blocks/
UTF-16 RFC文档(英文版):https://www.rfc-editor.org/rfc/rfc2781
UTF-16 RFC文档(翻译版):https://blog.youkuaiyun.com/jiangxinyu/article/details/2060576
专业术语:
- 代码单元 (code unit):在基本多语言平面中,每个字符用16位表示,通常称为代码单元。
- 码点 (code point):一个编码表中某个字符对应的代码值;在Unicode标准中,采用U+16进制数表示;一个码点可能不止一个代码单元;
- 代码平面 (code plane):Unicode有17个代码平面;第1个为经典的U+0000到U+FFFF(基本多语言平面,一个代码单元),其余16个为U+10000到U+10FFFF,包括辅助字符(用一对代码单元);
- 替代区域 (surrogate area):字符的值介于0x10000和0x10FFFF之间的,用一个值介于0xD800和0xDBFF(在所谓的高8位区)的16位整数和值介于0xDC00和0xDFFF(在所谓的低8位区)的16位整数来表示。
注意:最好不要用char,除非需要处理UTF-16代码单元,最好用字符串。char太底层了
替换区域
U+D800~U+DBFF表示第一个代码单元,U+DC00–U+DFFF表示第二个代码单元。
U+D800~U+DFFF,这一段码点称为 替换区域 ,可以看出它属于基本的多语言平面(U+0000~U+FFFF)的范围,为了避免产生歧义或冲突,替换区域的码点没有没配给任何字符。
假设为替换区域码点分配了字符,就无法判断该码点是表示一个字符,还是辅助字符的第一个代码单元或第二个代码单元。
那么,char如何表示码点在U+10000~U+10FFFF之间的Unicode字符呢?
它借鉴了UTF-16编码规则。
UTF-16使用不同长度的编码表示所有Unicode字符。在基本的多语言平面中,使用16位表示一个字符,称为代码单元,而其他平面的辅助字符需要使用32位,也就是一对代码单元表示一个字符。
U+D800U+DBFF表示第一个代码单元,U+DC00U+DFFF表示第二个代码单元。
U+D800U+DFFF,这一段码点称为替换区域,可以看出它属于基本的多语言平面(U+0000U+FFFF)的范围,为了避免产生歧义或冲突,替换区域的码点没有没配给任何字符。
假设为替换区域码点分配了字符,就无法判断该码点是表示一个字符,还是辅助字符的第一个代码单元或第二个代码单元。
具体让我们看一下如何表示字符 ‘⑪’ (Unicdoe字符码点 U+1D546)?
第一个代码单元:D800到DBFF
1101 10 00 0000 0000
1101 10 11 1111 1111
1101 10 XXXXXXXXXX 可以编码10位二进制数
第二个代码单元:DC00到DFFF
1101 11 00 0000 0000
1101 11 11 1111 1111
110111 XXXXXXXXXX 可以编码10位二进制数
U+10000到U+10FFFF 可以看出转换成二进制树最多有21位,但是我们加起来最多可以编码20位二进制数,怎么办?
将范围偏移-10000,范围变成了U+0000到U+FFFFF,如此,就满足20位编码了。
⑪ U+1D546 偏移后U+D546
1101 0101 0100 0110
后十位"01 0100 0110"放入第二个代码单元110111XXXXXXXXXX:1101 1101 0100 0110,对应的十六进制数:DD46
前十位(不足十位首部补0)00001101 01 放入第一个代码单元110110XXXXXXXXXX:1101 1000 0011 0101,对应的十六进制数是:D835
因此,⑪,U+1D546使用两个代码单元表示:D835 DD46
我们在程序中如何存储码点在U+10000到U+10FFFF之间的字符呢?
直接存储字符字面量值或者转义序列\u,编译报错。很容易理解char类型16位正好只能存储一个代码单元,而该字符需要两个代码单元表示,无法用char类型直接表示该字符。
码点与代码单元的区别:
参考:码点与代码单元
代码单元 就是一个字符的编码在存储时所占据的最小单位,或者说是编码的单位,一个编码会存储为1个、2个、4个代码单元,他们的大小以代码单元为单位进行递增。简单来说,代码单元的含义就是,字符编码的存储单位。如UTF-8的代码单元是一个字节,UTF-16的代码单元为两个字节,UTF-32的代码单元为4个字节。对于UTF-8和UTF-16这种边长字节编码,他们的单个字符的位不是固定的,而是根据代码单元进行扩展的。
码点 指的是:一个字符对应的编码;
在Java中,关于字符串字符数位的下标数字,它们的计量单位都是代码单元,输入的下标参数也好,输出的下标结果也好,它们都是以指定字符编码方式的代码单元为单位进行计数的,因此在遇到多代码单元表示的字符时,大概率出现错误。我们先从最基本的charAt开始看。
可以这么理解:一个char类型占2个字节,16位。可以表示大多数汉字,但有些特殊字符就占用两个char才能表示;每个char就是一个代码单元
举例:
String hello = "hi𝕆";//字符串中总共为4个char,3个代码点。
System.out.println(hello.length());//4
//codePointCount码点计算
hello.codePointCount(0, hello.length());//3
//字符串中下标为index的代码单元并转化成字符
hello.charAt(1);//i
//返回给定位置的码点
hello.codePointAt(1);//105 -\> i \u0069
//这个方法可以从一个字符串的某个位置为起始,返回往后推cpCount个字符位置的字符的索引
hello.offsetByCodePoints(0,3);
上面代码里的"hi𝕆",其中"𝕆"有两个代码单元,所以"hi𝕆"的长度为4,码点为3,所以charAt(index)方法最多写到3(因为索引的单位都是代码单元),但是charAt(3)返回的却不是"𝕆"而是"?",因为"𝕆"占两个代码单元,即占两个char,charAt只返回一个char,相当于截断了𝕆的码点。
offsetByCodePoints(int startIndex, int codePointOffset)看似没用,其实并非这么简单,hello.offsetByCodePoints(0,2)返回的是从第0个开始往后推2个的字符索引,所以结果是2,但hello.offsetByCodePoints(0,3)返回的却是3,追其原因是因为偏移的是码点,而返回的是代码单元;
替代区域:
在基本多语言平面中,每个字符用16位表示(代码单元),而辅助字符编码为一对连续的代码单元。采用这种编码所表示的每个值都属于基本多语言平面中未使用的2048个值范围,通常称为替代区域(U+D800U+DBFF用于第一个代码单元,U+DC00U+DFFF用于第二个代码单元,这样设计便于我们知道一个代码单元是一个字符的编码还是一个辅助字符的第一或第二部分。
相关函数
1.Character.charCount( int codePoint)
确定表示指定字符(Unicode码位)所需的字符值的数目。如果指定的字符等于或大于0x10000,则该方法返回2。否则,该方法返回1。
此方法不会将指定的字符验证为有效的Unicode码位。如果需要,调用方必须使用isValidCodePoint验证字符值。
2.isSurrogate(char ch)
public static boolean isSurrogate(char ch) {
return ch \>= MIN\_SURROGATE && ch \< (MAX\_SURROGATE + 1);
}
Determines if the given char value is a Unicode surrogate code unit.
Such values do not represent characters by themselves, but are used in the representation of supplementary characters in the UTF-16 encoding.
A char value is a surrogate code unit if and only if it is either a low-surrogate code unit or a high-surrogate code unit.
确定给定的字符值是否是Unicode代理代码单元。
这些值本身并不表示字符,而是在UTF-16编码中用于表示补充字符。
当且仅当char值是低代理代码单元或高代理代码单元时,它是代理代码单元。