一、编码概念总结:
1、概念:
字符集(character set):是一个系统支持的所有抽象字符的集合。
字符(character):是各种文字和符号,包括国家文字、标点符号、图形符号、数字等。
2、常见字符集:
字符集规定了符合对应的二进制代码,至于这个二进制代码如何存储则没有任何规定。就是为每个字符规定一个用来表示该字符的数字,仅此而已。
是一种字符编码规范
- Unicode:也叫统一字符集,它包含了几乎世界上所有的已经发现且需要使用的字符(如中文、日文、英文、德文等)。
- ASCII:早期的计算机系统只能处理英文,所以ASCII也就成为了计算机的缺省字符集,包含了英文所需要的所有字符。
- GB2312:中文字符集,包含ASCII字符集。ASCII部分用单字节表示,剩余部分用双字节表示。
- GBK:GB2312的扩展,完整包含了GB2312的所有内容。
- GB18030:GBK字符集的超集,常叫大汉字字符集,也叫CJK(Chinese,Japanese,Korea)字符集,包含了中、日、韩三国语言中的所有字符。
3、常见编码方式:
- UTF-8(Unicode字符集的编码方式)
- UTF-16(Unicode字符集的编码方式)
用2个字节为字符编码,可表示的字符数为2^16=65535 - UTF-32(Unicode字符集的编码方式)
用4个字节为字符编码(实际上只用了31位,最高位必须为0),有2^31=2147483648个码位 - ASCII(ASCII字符集的编码方式)
4、其他:
ASCII
是用来表示英文字符的一种编码规范,每个ASCII字符占用1个字节(8bits)
ASCII编码可以表示的最大字符数是256UTF:UCS Transformation Format,UCS转换格式
它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种:UTF-8和UTF-16 。
UTF-16和Unicode本身的编码规范(UCS-2(Unicode-16))是一致的
UTF-8定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容 。UTF-8有点类似于Haffman编码,它将Unicode编码为
00000000-0000007F的字符,用1 个字节表示
00000080-000007FF的字符,用2 个字节表示
00000800-0000FFFF的字符,用3 个字节表示
目前为止Unicode-16规范没有指定FFFF以上的字符,所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说,UTF-8最多需要用6字节表示一个字符。
5、中文编码:
国际上制定了针对中文的统一字符集GBK和GB18030,其中GBK已经在Windows、Linux等多种操作系统中被实现。
- GBK兼容GB2312,并增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎不兼容。
- GB18030相当于是GBK的超集,比GBK包含的字符更多。据我所知目前还没有操作系统直接支持GB18030。
二、计算机中的编码:
1、说明:
- 字符必须编码后才能被计算机处理。
- 内码:计算机使用的缺省编码方式就是计算机的内码。
- 按照程序员的称呼,GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。
在DBCS中,GB内码的存储格式始终是big endian,即高位在前。
GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。
2、编码规则:
根据上面编码规则对照表,进行 UTF-8 编码和解码就简单多了。下面以汉字“汉”为利,具体说明如何进行 UTF-8 编码和解码。
“汉”的 Unicode 码点是 0x6c49(110 1100 0100 1001),通过上面的对照表可以发现,0x0000 6c49 位于第三行的范围,那么得出其格式为 1110xxxx 10xxxxxx 10xxxxxx。接着,从“汉”的二进制数最后一位开始,从后向前依次填充对应格式中的 x,多出的 x 用 0 补上。这样,就得到了“汉”的 UTF-8 编码为 11100110 10110001 10001001,转换成十六进制就是 0xE6 0xB7 0x89。
解码的过程也十分简单:如果一个字节的第一位是 0 ,则说明这个字节对应一个字符;如果一个字节的第一位1,那么连续有多少个 1,就表示该字符占用多少个字节。
三、Java中的字符编码和字符集
1、char字符
- Java中char类型是16位无符号基本数据类型,用来存储Unicode字符,使用UTF-16编码描述一个代码单元。
- Unicode代码点的合法范围是 :U+0000到U+10FFFF
U+0000到U+FFFF称为Basic Multilingual Plane(BMP)
代码点大于U+FFFF的字符称为增补字符,2个char:一个为高位,一个为低位。
2、String字符串
- Java字符串由char序列组成
- String类型的length方法,它返回的是UTF-16编码表示的给定字符串的代码单元的数量
如果想要得到代码点的数量,可以调用codePointCount()方法,charAt方法返回位于指定位置的代码单元,codePointAt方法则返回指定位置的代码点。 - Java代码需要编译成class文件后由JVM运行,在class文件里,字符串使用UTF-8编码,保存于常量池中。
4、Big Endian,Little Endian与文本开头的标志
- 一个字符可能占用多个字节,比如字符0xABCD,如果存储为AB CD,则称为Big Endian;如果存储为 CD AB,则称为Little Endian。
-
要知道具体是哪种编码方式,需要判断文本开头的标志:
5、字节流与字符流
- InputStream为字节输入流的所有类的超类
- Reader为读取字符流的抽象类
- java读取文件的方式分为按字节流读取和按字符流读取,其中InputStream、Reader是这两种读取方式的超类。
- 其实字符流可以看做是一种包装流,它的底层还是采用字节流来读取字节,然后它使用指定的编码方式将读取字节解码为字符。在读取的时候字符读取每次是读取2个字节,字节流每次读取1个字节。
四、相关面试题:
1、Java中的char是两个字节,如何存UTF-8字符?
- char是两个字节(16位),是用UTF-16来存储一个代码单元,而UTF-8是可变编码方式,存1到3个字节
-
UTF-8是Unicode字符集的一种常见的编码方式,另一种常见的编码方式是UTF-16(最小单位是2个字节)。
- Unicode通用字符集占两个字节,如“中”
- Unicode扩展字符集就需要用一对char来表示,如表情“😎”
- Java中的String的length得到的不是字符数,而是字节数
- 比如“中”这个字符串,以“UTF-16”的编码方式获得的length长度为4,其中前两个字节表示的是字节序标记,说明是后两个字节要一起解析。
具体的例子见:结合Java详谈字符编码和字符集
Java9中对字符串做了些优化,字符串长度和字符数不一致未做处理,对字符存储空间做了优化,对Latin(拉丁)字符,还用UTF-16存储,就浪费了一半的空间,就用byte存,就可以节省一半的空间。
资料:
Java:Unicode简介
彻底弄懂 Unicode 编码
结合Java详谈字符编码和字符集
java编码之BASE64
Java中的字节,字符与编码,解码
Java Unicode编码系统