Java技术文章Java学习笔记

java中一个汉字占用几个字节

2018-05-21  本文已影响82人  啥也不说了

最近看了Java编程的逻辑 (6) - 如何从乱码中恢复 (上),突然想起一个面试题——char型变量中能不能存贮一个中文汉字。就想确定下一个汉字到底占几个字符?

结论

结论概述

char类型可以存储一个中文汉字。因为Java中char的编码方式为UTF-16BE。UTF-16编码使用2或者4字节,在65536以内的占两个字节。而基本上所有中文的Unicode编码在19968到40869之间——既Unicode至少包含了20902个汉字,所以一个char类型可以存储一个汉字。

是否有超出65536的汉字,我不知道。如果有人知道可以评论一下。

不同编码占用的存储空间

编码方式 占用空间 说明
ASCII 不能存储中文
ISO 8859-1或Windows-1252 不能存储中文
GB2312 2字节 约7000个汉字,不包括一些罕用词,不包括繁体字
GBK 2字节 向下兼容GB2312,GBK增加了一万四千多个汉字,共计约21000汉字,其中包括繁体字
GB18030 2字节或4字节 GB18030向下兼容GBK,增加了五万五千多个字符,共七万六千多个字符。包括了很多少数民族字符,以及中日韩统一字符
Big5 2字节 包括1万3千多个繁体字,和GB2312类似
UTF-32 4字节 肯定可以存储汉字,不过使用比较少
UTF-16 2字节或4字节 基本上汉字占用两个字节
UTF-8 1-4字节,根据存储大小选择。前面提到的中文基本都是3个字节 最常用的

计算UTF-8存中文时占空间大小的代码

List<Integer> byteLengths = new ArrayList<>();
for (int i = 19968; i <= 40869; i++) {
    char c = (char) i;
    byteLengths.add(String.valueOf(c).getBytes("utf-8").length);

Map<Integer, Long> collect = byteLengths.stream().collect(groupingBy(Integer::intValue, counting()));

System.out.println(collect);

汇总

通过上面的讨论,可以得出两个结论:

编码方案

  1. ASCII
  2. ISO 8859-1或Windows-1252
  3. 中文的GB2312,GBK,GB18030和Big5
  4. Unicode

上面四种方案,其中第三和第四种都可以用来表示汉字,后面分别来看看不同的字符方案占用的存储空间大小。

所以,会发现使用不同的编码方案,就会有不同的占用空间大小。

占用的字节

GB2312

GB2312标准主要针对的是简体中文常见字符,包括约7000个汉字,不包括一些罕用词,不包括繁体字。

GB2312固定使用两个字节表示汉字,在这两个字节中,最高位都是1,如果是0,就认为是Ascii字符。在这两个字节中,其中高位字节范围是0xA1-0xF7,低位字节范围是0xA1-0xFE。

GBK

GBK建立在GB2312的基础上,向下兼容GB2312,也就是说,GB2312编码的字符和二进制表示,在GBK编码里是完全一样的。

GBK增加了一万四千多个汉字,共计约21000汉字,其中包括繁体字。
GBK同样使用固定的两个字节表示,其中高位字节范围是0x81-0xFE,低位字节范围是0x40-0x7E和0x80-0xFE。

需要注意的是,低位字节是从0x40也就是64开始的,也就是说,低位字节最高位可能为0。那怎么知道它是汉字的一部分,还是一个Ascii字符呢?

其实很简单,因为汉字是用固定两个字节表示的,在解析二进制流的时候,如果第一个字节的最高位为1,那么就将下一个字节读进来一起解析为一个汉字,而不用考虑它的最高位,解析完后,跳到第三个字节继续解析。

GB18030

GB18030向下兼容GBK,增加了五万五千多个字符,共七万六千多个字符。包括了很多少数民族字符,以及中日韩统一字符。

用两个字节已经表示不了GB18030中的所有字符,GB18030使用变长编码,有的字符是两个字节,有的是四个字节。

在两字节编码中,字节表示范围与GBK一样。在四字节编码中,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节的值从0x81到0xFE,第四个字节的值从0x30到0x39。

解析二进制时,如何知道是两个字节还是四个字节表示一个字符呢?看第二个字节的范围,如果是0x30到0x39就是四个字节表示,因为两个字节编码中第二字节都比这个大。

Big5

Big5是针对繁体中文的,广泛用于台湾香港等地。

Big5包括1万3千多个繁体字,和GB2312类似,一个字符同样固定使用两个字节表示。在这两个字节中,高位字节范围是0x81-0xFE,低位字节范围是0x40-0x7E和0xA1-0xFE。

Unicode

Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

UTF-8

UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。

存储范围1-4字节都有。

UTF-32

这个最简单,就是字符编号的整数二进制形式,四个字节。

但有个细节,就是字节的排列顺序,如果第一个字节是整数二进制中的最高位,最后一个字节是整数二进制中的最低位,那这种字节序就叫“大端”(Big Endian, BE),否则,正好相反的情况,就叫“小端”(Little Endian, LE)。对应的编码方式分别是UTF-32BE和UTF-32LE。

可以看出,每个字符都用四个字节表示,非常浪费空间,实际采用的也比较少。

UTF-16

UTF-16使用变长字节表示:

区分是两个字节还是四个字节表示一个符号就看前两个字节的编号范围,如果是U+D800到U+DBFF,就是四个字节,否则就是两个字节。

UTF-16也有和UTF-32一样的字节序问题,如果高位存放在前面就叫大端(BE),编码就叫UTF-16BE,否则就叫小端,编码就叫UTF-16LE。

UTF-16常用于系统内部编码,UTF-16比UTF-32节省了很多空间,但是任何一个字符都至少需要两个字节表示,对于美国和西欧国家而言,还是很浪费的。

参考文档

  1. Java编程的逻辑 (6) - 如何从乱码中恢复 (上)?
  2. 字符编码笔记:ASCII,Unicode 和 UTF-8
上一篇 下一篇

猜你喜欢

热点阅读