个人反思
Java中有Character、String、StringBuilder等用于文本处理的类,但这些类的基础都是char
以前我对char的理解总是浮于表面,知道char能表示一个字符(字母或中文字符等),也知道有Unicode编码,也知道char能转成int类型,但对char没有一个更深入的了解。这篇文章算是自己对char的一个深入总结,将以上知识点串联起来。
字符的编码
编码有两大类:Unicode编码和非Unicode编码 (了解即可)
非Unicode编码:
- ASCII码:自然是熟悉不过的,ASCII码使用了128个字符基本表示了美国的标准字符。用了从0~127刚好128个十进制数来表示,刚好占用了7个bit位。而一个字节有8位,所以最高位设为0表示这是ASCII编码。
- GBK码:GBK使用了两个字节来表示一个字符,其中高位字节范围是
0x81~0xFE
,低位字节范围0x40~0x7E
和0x80~0xFE
。前面写到:ASCII编码最高位为0,其他编码最高位设置为1。这里需要注意的一点是:由于GBK低位编码表示范围从0x40(64)开始,所以低位编码的最高位可能为0。所以,为了区分这是GBK还是ASCII,在读入第一个字节时,如果高位为1,那么直接将下一个字节也读入进行共同解析。
Unicode编码:
这里主要介绍UTF-8
UTF-8使用可变长字节来表示一个字符,具体可参考下表[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KeK3CBwq-1644569389530)(2)]
小于128的,编码与ASCII码一样,最高位为0。其他编号的第一个字节有特殊含义,最高位有几个连续的1就表示用几个字节表示,而其他字节都以10开头。
这里举一个例子来表示一个字符的Unicode编码
通过Unicode工具查询到字符’馨’的十六进制数为99A8(39336)。按照上表的范围可知,需要3个字节进行表示(当然,这里也可以通过写出2进制,倒推出具体需要多少位)。则其二进制格式为
1110xxxx 10xxxxxx 10xxxxxx
0x99A8的二进制为
1001 100110 101000
将上面的二进制数依次填入Unicode格式中即可得到字符’馨’的UTF-8编码
11101001 10100110 10101000
char
*char本质上是一个占用两个字节的无符号正整数。*在Java内部进行字符处理时,采用的都是Unicode编码。所以char只能表示Unicode编号在65536以内的字符(存储原编号的二进制数)。超出这个范围的字符,就只有使用2个char或String、Character来进行表示。
总结
在此基础上,上文的多个知识点也就被串联了
以下是char的赋值
char a = 'A';
char b = '馨';
char c = 39336;
char d = 0x99A8;
char e = '\u99A8';
以上代码,除了a是’A’,其他的4种赋值方式表示的字符都是’馨’。
基于以上编码知识,能初步认识到乱码无非就两种情况:
- 解析错误:一个GBK编码的字符,用Unicode进行解析,得到的可能就是一个乱码
- 错误解析与编码转换:
(1)字符串“丁香花”,本来的编码格式是GB18030,编码(十六进制)是b6a1cfe3bba8
(2)这个二进制形式被错误当成了Windows-1252编码,解读成了"¶¡Ï㻨"(java可参考在Java中使用Windows-1252)
(3)随后这个字符进行了编码转换,转换成了UTF-8编码,形式还是"¶¡Ï㻨",但十六进制变成了c2b6c2a1c38fc3a3c2bbc2a8
(4)这个时候再按照GB18030解析,字符就变成了乱码形式"露隆脧茫禄篓",而且这时无论怎么切换查看编码的方式,这个结果看起来都是乱码。
PS UTF-8 编码格式补充
- 一字节:0xxxxxxx
- 二字节:110xxxxx 10xxxxxx
- 三字节:1110xxxx 10xxxxxx 10xxxxxx
- 四字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx