聊聊字符編碼

你是否有過這樣的經歷,文件在別人電腦上看着好好的,可是拷貝到本身的電腦上就發現亂碼了。或者做爲程序員的你,打開別人的代碼時,發現中文註釋亂碼了,而代碼缺一點毛病都沒有。我曾經也被這些問題困擾許久,雖然不少時候轉換一下字符編碼問題就解決了,可是背後的緣由卻一直縷不順。所以,想借這篇文章,儘量把這件事情給說清楚。程序員

字符編碼的起源-ASCII碼

計算機存儲數據都是以二進制的形式進行存儲的,可是二進制形式人類是沒法直接解讀的。所以,對於保存在計算機上的文本數據,須要一張映射表實現二進制與文字之間的相互轉換。好比,能夠約定0000 0001表明字母A,0000 0002表明字母B,所以當你保存一段文本ABA時,實際上計算機存儲的是0000 0001 0000 0002 0000 0001。相應的,當你打開這個文件時,本質讀取的二進制數據,可是編輯器會將這段二進制碼查錶轉換成ABA編輯器

計算機問世的早期,有這樣一張映射表被創建做爲編解碼的標準,稱爲ASCII碼。 ide

ASCII碼
如上圖所示,ASCII碼用7個bit位表示,範圍從0~127,一共128個字符。在計算機問世的早期,主要只有英文符號,並且存儲空間十分有限,所以用一個字節就可以知足需求了。可是隨着計算機的快速普及,各國都有將本國文字進行輸入輸出的需求,可是ASCII碼沒法知足這樣的需求,所以須要新的編碼。

百花齊放-多字符集

對於拉丁語系國家而言,他們能夠擴展ASCII碼的第8個bit位來知足本國的編碼需求,可是對於非拉丁語系的國家而言,單字符集(只用了一個字節完成編碼的稱爲單字符集,對應的就是多個字節編碼的成爲多字符集)沒法知足編碼本國語言的需求,所以他們用多個字節來表示一個文字。例如,咱們國家就是使用的雙字節字符編碼,使用最爲普遍的就是GB2312編碼。可是GB2312只能編碼簡體中文,所以出現了GBK編碼,GBK編碼除了支持簡體中文,還支持繁體中文以及日語、韓語等的編碼,是大一統的編碼。ui

GBK的編碼規則 GBK編碼使用1-2個字節進行編碼,GBK編碼分爲不少個碼頁,每一個碼頁的範圍編碼的範圍都是一個字節,即0x00-0xFF。GBK編碼先經過第一個字節查詢第一張表(以下圖)。 編碼

若是第一個字節的首位爲0,也就是範圍在0x00-0x80之間時,直接從該表中查詢獲得對應的字符,和傳統的ASCII碼查詢的方式相同。例如百分號%的編碼就是0x25。spa

若是第一個字節的首位爲1,也就是範圍在0x81-0xFF之間時,先查詢該表獲得第二個字節須要查詢的編碼頁的頁碼號。例如漢字的編碼爲8144,第一個字節先查詢到要查第0x81編號的頁碼,而後在0x81編號的頁碼中查詢編碼爲0x44的對應的文字就是code

因爲各國都有本身的編碼,並且編碼的方式還不少,規則的不統一致使,文本轉化中會很麻煩,所以制定了ANSI標準,各國指定標準的多字符集編碼方式,例如我國的標準編碼就是GB2312。cdn

全球一統-UNICODE

因爲各國都制定了本國的多字節的字符編碼,致使全球範圍內的字符字符編碼集很是多,這會使得各國之間文字轉換很是的麻煩。所以大佬們坐下約定了一個全球統一的編碼,用一個編碼表示全世界全部的文字的,這就是UNICODE編碼。UNICODE編碼是兩個字節,所以能夠編碼256*256個字符,這基本能夠知足全球字符編碼的需求了。blog

咱們以漢字的爲例,其Unicode編碼爲\u6c49\u用來標識其爲Unicode編碼,由0x6c0x49組成。用兩個字節表示,那麼就有前後順序的問題,6c49496c兩種方式都能表示,所以兩種不一樣順序的編碼的編碼方式稱爲大端和小端模式字符串

Unicode實際上仍是一個比較廣義的概念,在實際編碼規則中常見的有UCS-2,UTF-16,UTF-8。接下來咱們分別闡述這幾種編碼的概念。

UCS-2與UTF-16

UCS-2是Unicode編碼的標準實現,全部的字符都是按照兩個字節編碼。兩個字節的順序不一樣就產生了USC-2大端和USC-2小端兩種模式。可是UCS-2只編碼了BMP字符,而UTF16則經常使用變長的方式來兼容其餘字符,最短兩個字符。BMP字符UTF16和UCS-2是相同的,擴展的部分則用四個字節編碼。

UTF-8

終於到了咱們最熟悉的UTF-8了。Unicode編碼出現後,使用拉丁文的國家發現本身吃了大虧,他們認爲拉丁文本來只須要一個字節就能夠編碼,如今卻須要兩個字節,所以搞出了變長的UTF-8字符。

UTF-8的編碼規則

[1] UTF-8是變長度的,長度爲1-6個字節;

[2] 第一個字節的連續的二進制位值爲1的個數決定了其編碼的位數

[3] 若是第一個字節以0開頭說明是單個字節的字符

[4] 對於非單個字節的字符,出首字節外,其他均已10開頭

上面的規則用編碼表示更加清晰,以下:

佔用字節 首字節大小 完整表示
1字節 大於0 0xxxxxxx
2字節 大於0xc0 110xxxxx 10xxxxxx
3字節 大於0xe0 1110xxxx 10xxxxxx 10xxxxxx
4字節 大於0xf0 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字節 大於0xf8 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字節 大於0xfc 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

根據這個編碼規則,就能夠判斷出一個字符串是由多少個字符組成,C++代碼以下:

inline std::string GetHideName(const std::string& sUtf8Data) {
    std::vector<std::string> vName;
    
    std::string ch; 
    for (size_t i = 0, len = 0; i != sUtf8Data.length(); i += len)
    {
        unsigned char byte = (unsigned)sUtf8Data[i];
        if (byte >= 0xFC) // lenght 6
            len = 6;
        else if (byte >= 0xF8)
            len = 5;
        else if (byte >= 0xF0)
            len = 4;
        else if (byte >= 0xE0)
            len = 3;
        else if (byte >= 0xC0)
            len = 2;
        else
            len = 1;
        ch = sUtf8Data.substr(i, len);
        vName.push_back(ch);
    }   
    
    std::string sQxName;
    if (vName.size() <= 2)
    {
        sQxName = vName.size() > 0?(vName.front() + "*"):sUtf8Data;
    }
    else
    {
        sQxName = vName.front() + "**" + vName.back();
    }
    return sQxName;
}
複製代碼

問題

[1] GBK與GB2312的區別? GBK是GB2312的超集,你能夠簡單這樣理解,GB2312編碼的是簡體中文,而GBK在GB2312的基礎上增長了繁體字以及日語和韓語。由於GBK是在GB2312的基礎上進行擴展的,因此簡體中文用這二者是相同的編碼。

相關文章
相關標籤/搜索