Okio原理分析之字元編碼
技術標籤:開源框架分析okio編碼utf-8utf-16GBK
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 編碼