OKio的原理分析,准备分3个部分来分析:
- 字符编码 先了解一些背景知识,Okio里面基本上是基于UTF-8来编码实现的
- Okio简介 熟悉OKio里面引入的一些概念,如Source、Sink、Timeout、Buffer、Segment、SegmentPool、ByteString等
- Okio里面Segment数据移动管理 OKio高效的原因,在数据移动方面的一些巧妙的设计,来节约内存和节省CPU
1.字符编码
字符编码(Character encoding)、字集码是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII。其中,ASCII将字母、数字和其它符号编号,并用7比特的二进制来表示这个整数。通常会额外使用一个扩充的比特,以便于以1个字节的方式存储。
在计算机系统里面,编码的原因可以总结为:
- 计算机中存储信息的最小单元是一个字节即 8 个 bit,所以能表示的字符范围是 0~255 个
- 人类要表示的符号太多,无法用一个字节来完全表示
- 要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码
UCS和Unicode
通用字符集(英语:Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。
通用字符集包括了其他所有字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码,你不会丢失任何信息。
Unicode,中文又称万国码、国际码、统一码、单一码,是计算机科学领域里的一项业界标准
Unicode规范定义了,每个文件的最前面分别加入一个表示编码顺序的字符,用FEFF来表示,正好是2个字节。如果文本文件的头2个字节是FEFF,表示用的大头方式(第一个字节在前),用FFFE的表示用的小头方式(第二个字节在前)
Unicode是一个符号集合(字符集),定义了符号的二进制代码,但是没有规定如何存储二进制代码
Unicode和UCS是来自2个不同的组织,Unicode对集合添加了一些新的规则和规范
历史上存在两个独立的尝试创立单一字符集的组织
- 国际标准化组织(ISO)于1984年创建的ISO/IEC
- 由Xerox、Apple等软件制造商于1988年组成的统一码联盟。前者开发的ISO/IEC 10646项目,后者开发的Unicode项目。因此最初制定了不同的标准。
从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode一般都会采用有关字码最常见的字体,但ISO 10646一般都尽可能采用Century字体
UTF
Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)。
UTF是"Unicode/UCS Transformation Format"的首字母缩写,即把Unicode字符转换为某种格式之意。UTF-16正式定义于ISO/IEC 10646-1的附录C,而RFC2781也定义了相似的做法。
编码格式
- ASCII :美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。规定了128个字符的编码,用一个byte来表示,只占用7个bit,最高为0
- ISO-8859-1:ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符
- GB2312 双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字
- GBK 它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。
- UTF-16 是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。
- UTF-8:是Unicode的实现方式之一,使用变长的编码形式,可以用1-6个byte来表示一个符号(In UTF-8, characters are encoded using sequences of 1 to 6 octets.)
UTF-8 有以下编码规则:
- 如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 – 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
- 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
- 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000-7FFF FFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
详细的编码说明可以参考:https://tools.ietf.org/html/rfc2279
2.Java里面的编码说明
Java字符和字节
- 二进制位bit:计算机,所有的信息都是bit的形式存在,每个bit有0和1两种状态
- 字节byte:8个二进制位是一个byte,1 byte = 8 bit
- 字符Character:1 char = 2 byte = 16 bit(Java对字符默认是UTF-16编码)
此节内容,截取自深入分析 Java 中的中文编码问题
以字符串”I am 君山”的 char 数组为 49 20 61 6d 20 541b 5c71,为例,下面把它按照不同的编码格式转化成相应的字节。
public static void encode() {
String name = "I am 君山";
System.out.println( " default charset = " + Charset.defaultCharset()+" : " +bytesToHex(name.getBytes()));
try {
byte[] iso8859 = name.getBytes("ISO-8859-1");
System.out.println("ISO-8859-1 = " + bytesToHex(iso8859));
byte[] gb2312 = name.getBytes("GB2312");
System.out.println("GB2312 = " + bytesToHex(gb2312));
byte[] gbk = name.getBytes("GBK");
System.out.println("GBK = " + bytesToHex(gbk));
byte[] utf16 = name.getBytes("UTF-16");
System.out.println("UTF-16 = " + bytesToHex(utf16));
byte[] utf8 = name.getBytes("UTF-8");
System.out.println("UTF-8 = " + bytesToHex(utf8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String bytesToHex(byte[] bytes) {
char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
执行结果是:
default charset = UTF-8 : 4920616D20E5909BE5B1B1
ISO-8859-1 = 4920616D203F3F
GB2312 = 4920616D20BEFDC9BD
GBK = 4920616D20BEFDC9BD
UTF-16 = FEFF004900200061006D0020541B5C71
UTF-8 = 4920616D20E5909BE5B1B1
按照 ISO-8859-1 编码
按照 GB2312 编码
按照 GBK 编码
按照 UTF-16 编码
按照 UTF-8 编码