你是否有過這樣的經歷,文件在別人電腦上看着好好的,可是拷貝到本身的電腦上就發現亂碼了。或者做爲程序員的你,打開別人的代碼時,發現中文註釋亂碼了,而代碼缺一點毛病都沒有。我曾經也被這些問題困擾許久,雖然不少時候轉換一下字符編碼問題就解決了,可是背後的緣由卻一直縷不順。所以,想借這篇文章,儘量把這件事情給說清楚。程序員
計算機存儲數據都是以二進制的形式進行存儲的,可是二進制形式人類是沒法直接解讀的。所以,對於保存在計算機上的文本數據,須要一張映射表實現二進制與文字之間的相互轉換。好比,能夠約定0000 0001
表明字母A,0000 0002
表明字母B,所以當你保存一段文本ABA
時,實際上計算機存儲的是0000 0001 0000 0002 0000 0001
。相應的,當你打開這個文件時,本質讀取的二進制數據,可是編輯器會將這段二進制碼查錶轉換成ABA
。編輯器
計算機問世的早期,有這樣一張映射表被創建做爲編解碼的標準,稱爲ASCII碼。 ide
對於拉丁語系國家而言,他們能夠擴展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編碼是兩個字節,所以能夠編碼256*256個字符,這基本能夠知足全球字符編碼的需求了。blog
咱們以漢字的漢
爲例,其Unicode編碼爲\u6c49
,\u
用來標識其爲Unicode編碼,由0x6c
和0x49
組成。用兩個字節表示,那麼就有前後順序的問題,6c49
和496c
兩種方式都能表示,所以兩種不一樣順序的編碼的編碼方式稱爲大端和小端模式字符串
Unicode實際上仍是一個比較廣義的概念,在實際編碼規則中常見的有UCS-2,UTF-16,UTF-8。接下來咱們分別闡述這幾種編碼的概念。
UCS-2是Unicode編碼的標準實現,全部的字符都是按照兩個字節編碼。兩個字節的順序不一樣就產生了USC-2大端和USC-2小端兩種模式。可是UCS-2只編碼了BMP字符,而UTF16則經常使用變長的方式來兼容其餘字符,最短兩個字符。BMP字符UTF16和UCS-2是相同的,擴展的部分則用四個字節編碼。
終於到了咱們最熟悉的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的基礎上進行擴展的,因此簡體中文用這二者是相同的編碼。