計算機技術核心 —— 字符編解碼

  • 在計算機的世界裏,所有的事物都是由 0101 的二進制數字構成
  • 如何將現實世界中的事物與 0101 對應起來,這是計算機技術的基石之一

0 字符集與編解碼

  • 字符集: 一個字典,收集了很多字符(文化概念,即符號),並給每個符號,確定了一個唯一的代號(code,爲數值)
  • 編碼: 將一個字符集中的代號,編碼成 2 進制形式
  • 解碼: 將 2 進制形式的 byte 流,解碼成 代號 形式

1 幾種常見的字符集

1.1 ASCII

  • ASCII 既是字符集,也代表編碼方案

  • ASCII 碼,總共有 128 個,用一個字節的低 7 位表示

  • 0~31 是控制字符如換行回車刪除等;32~126 是打印字符

1.2 ISO-8859-1

  • ISO-8859-1既是字符集,也代表編解碼方案

  • 正式編號爲 ISO/IEC 8859-1:1998,又稱 Latin-1 或 西歐語言

  • 它以 ASCII 爲基礎,在空置的0xA0-0xFF的範圍內,加入96個字母 及 符號

  • ISO-8859-1 仍然是單字節編解碼,它總共能表示 256 個字符

  • ISO-8859-1對應於ISO/IEC 10646即 Unicode 的前256個碼位

    • 所以 unicode 字符集和 ISO-8859-1 在前 256 個碼位是可以通用的
    • 只要超過這個碼位,就會出現亂碼
  • 如果文件中只存在 256 碼位之前的,用 ISO-8859-1 將更省帶寬

<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<%@ page contentType="text/html;charset= iso-8859-1" %>

1.3 GBK

  • GBK 是 GB 2312 的超集,只是字符集
  • 它們的編解碼都是基於 EUC-CN 的
  • 字符有一字節和雙字節編解碼,007F 範圍內是第一個字節,和 ASCII 保持一致
  • 如果是雙字節字符,第一個字節肯定爲高字節

1.4 Unicode

1.4.1 簡介

  • Unicode擴展了code point 的範圍,遠遠超出了ISO-8859-1和字節存儲的8位限制。幾乎所有的口頭和書面語言、數學和其他符號以及歷史語言都指定了Unicode字符

1.4.2 code points

  • code points 代表了在 unicode 字符集中,分配給每個字符的數值

1.4.3 Unicode Planes

  • 一個 plane 由 65,536 (216) 個 code points 組成
  • unicode 共有 17 個 plane
1.4.3.1 BMP
  • The first plane, plane 0, the Basic Multilingual Plane (BMP) contains characters for almost all modern languages, and a large number of symbols
1.4.3.2 Supplementary Multilingual Plane (SMP) U+1000 – U+1ffff
  • 補增的多語言平面
  • 主要用於歷史劇本、音樂和數學符號
1.4.3.3 Higher Planes
  • 更高的平面

2 幾種常見的編解碼方式

2.1 ASCII

2.2 ISO-8859-1

2.3 EUC-CN

2.4 UTF-32

  • UTF-32 是,Unicode 字符集的一種編解碼方式
  • 定長編解碼,每個字符都由 4 個字節來表示,非常簡單,高位補零即可
  • 是 USC-4 的子集,編解碼範圍限制在0~0x10FFFF之間
  • 優點:編解碼速度快
  • 缺點:浪費空間(不考慮壓縮)

2.5 UTF-16

  • UTF-16 是,Unicode 字符集的一種編解碼方式
  • 變長編解碼,一般情況下都是 2 個字節表示一個字符(code point 在 0-FFFF 之間)
  • 如果超過 FFFF 則將使用 2 個 16字節(a surrogate pair of 16 bit,2 個 code unit) 表示 一個字符
  • 注: java 中的 char 就是 2 個字節的,即 UTF-16 解碼形式
  • 注: java 中對 String 的編解碼操作,最終都會以 UTF-16 的解碼形式存儲到 char 數組中
    @Test
    public void testUtf() {
        //str 內部存儲的是 utf-16 形式的 code unit
        String str = "i am 康康";
        try {
            //將 str 的 code unit 編碼爲 gbk 形式的 bytes,然後在通過 gbk 解碼 bytes
            //關鍵是直接解碼成 utf-16 形式的 code unit!這裏並不是解碼成 gbk 對應的 數值碼
            //不會亂碼
            String str1 = new String(str.getBytes("gbk"),"gbk");
            //通過平臺默認編碼(java 爲 utf-8) 將 str 編碼成 utf-8 對應的 bytes
            //然後,通過 utf-8 將 byte 直接解碼成 utf-16 的 code unit 形式
            //不會亂碼
            String str2 = new String(str.getBytes(),"utf-8");
            //通過平臺默認編碼(java 爲 utf-8) 將 str 編碼成 utf-8 對應的 bytes
            //然後通過 utf-16 解碼 bytes,這裏就會亂碼了,因爲 bytes 對應的編解碼不一樣
            String str3 = new String(str.getBytes(),"utf-16");
            //通過 ISO-8859-1 編碼 將 str 編碼成對應的 bytes
            //然後通過 ISO-8859-1 解碼
            //會亂碼,雖然 bytes 對應的編解碼一樣,但是在 str 編碼的時候 用的 ISO-8859-1 它的編解碼範圍是 255,str 中 康對應的 code unit 數值已經大於 255 了,造成了編碼時,把超過範圍的 code 編碼成了 3f,這時對 3f 解碼時,會解碼爲 ?
            String str5 = new String(str.getBytes("ISO-8859-1"),"ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

2.6 USC-2

  • USC-2 是,Unicode 字符集的一種編解碼方式
  • 定長編解碼,每個字符由 2 個字節表示
  • 和 UTF-16 的區別在於,它不支持 surrogate pair
  • 在 BMP 上,它們兩可以互換

2.7 UTF-8

  • UTF-8 是,Unicode 字符集的一種編解碼方式

  • 是一種存儲效率很高的編碼方式

    • ASCII characters 0-127 (decimal) are encoded as a single byte
    • Code points U+80 to U+7FF are stored as 2 bytes.
    • Code points U+800 to U+FFFF are stored as 3 byte. 中文的 unicode 範圍:4E00-9FA5(常用部分)
    • Code points U+10000 U+10FFFF are stored as 4 bytes.
  • unicode <—> utf-8 對照表(很有規律)

字節數 Unicode UTF-8編碼
1 000000-00007F 0xxxxxxx
2 000080-0007FF 110xxxxx 10xxxxxx
3 000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 010000-10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

3 Little endian 和 Big endian 與 UTF-8 BOM

  • 計算機的發送信息的最小單位是 字節
  • 這使得所有以多個字節爲單位的編解碼方案會出現 字節順序的問題,如 UTF-16,UTF-32 等(不包括 UTF-8)
  • 第一個字節在前,就是"大頭方式"(Big endian)
  • 第二個字節在前就是"小頭方式"(Little endian)
  • 在保存爲UTF-8的任何文檔的開頭都添加了字節0xEF,0xBB,0xBF。這是Unicode 字節順序標記(BOM)的UTF-8編解碼,並且即使字節順序與UTF-8無關,也通常稱爲UTF-8 BOM。
  • Unicode標準既不要求也不建議使用UTF-8的BOM,但是警告說,從另一種編碼轉碼的文件的開頭可能會遇到BOM
  • UTF-8 BOM的存在可能會導致可以處理UTF-8的現有軟件出現問題(不要使用!)

4 在 Windows 記事本的語境中

  • 所謂的「ANSI」指的是對應當前系統 locale 的遺留(legacy)編碼
  • 所謂的「Unicode」指的是帶有 BOM 的小端序 UTF-16
  • 所謂的「UTF-8」指的是帶 BOM 的 UTF-8

參考

A Beginner’s Guide to Understanding Unicode for the Application Programmer and Web Developer
Working with ISO-8859-1 and Unicode Character Sets
細說:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4
深入分析 Java 中的中文編碼問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章