背景
需要寫一個漢字轉拼音的程序,參考了網上的代碼,沒一個能正常工作的。後來發現是字符集和編碼的鍋,下面做一個總結。
字符編碼的由來
1.電腦上的字符本質上是像素點組成的圖案
,最開始IBM個人電腦普及的時候,電腦程序附帶了一張字符集,裏面是各種字符的顯示圖案,電腦要顯示一個字符就去找對應位置序號
的圖案,然後原樣顯示出來,簡單講就是一個 查表的過程
。
2.剛開始只有美國人有電腦,於是ANSI字符集誕生了,後來歐洲也開始普及了,需要顯示意大利語,法文,ANSI字符集裏面的127個字符是不夠用的,於是需要擴充,剛開始7個Bit就能組合出所有的英文字符和符號,於是ISO組織擴展了第8位,這樣就有了 Latin-1
字符集,也叫 ISO-8859-1
,就能表示出255個字符組合,顯示意大利語,法文不再是難題;最後輪到亞洲國家,尤其是發展中的中國,龐大的漢字體系,還分簡體,繁體,藏文,這把美國人嚇尿了. 於是就有了後來的 GB2312,GBK,BIG5(繁體字),GB18030,UNICODE, UTF8
, 這樣一來字符數越多,每個字符佔用的位數就要相應增加. 下面是各種字符集的簡要說明.
字符集對比
字符集 | 簡介 | 每個字符佔用位寬 |
---|---|---|
ANSI |
收入英文字母,數字和符號共127個 | 單字節,實際只用了低7位,最高位爲0 |
ISO-8859-1 |
收入英文字母,數字和符號共256個,兼容 ANSI |
單字節 |
GB2312 |
收入簡體漢字 6763 個和非漢字圖形字符 682 個 |
雙字節 |
GBK |
共 23940 個碼位,收錄了 21003 個簡體漢字,兼容 GB2312 |
雙字節 |
GB18030 |
收入簡體漢字,包括少數民族文字 共計 70,244 個 |
多字節 |
BIG5 |
收入繁體漢字 13,060 個 |
雙字節 |
UTF-8 |
用1到4個字節編碼Unicode字符 | 多字節,省存儲空間,易傳輸 |
UTF-16 |
以1個或者2個16位長編碼UNICODE字符 | 1個雙字節,或者2個雙字節 |
UTF-32 |
對每一個Unicode碼位使用恰好32Bit | 四個字節 |
UNICODE |
跨語種解決方案,可以容納世界上所有的文字符號 | 雙字節 |
注: 所謂 碼位
就是字符在對應字符集所處位置(序號).
編譯器對中文的測試
wxString str1 = wxT("中國人");
std::wstring str2= L"中國人";
std::cout<<"str1 = "<<str1<<std::endl;
std::cout<<"str2 = "<<str2<<std::endl;
std::wcout<<"str2_ ="<<str2<<std::endl;
codeblocks | VS2018 | |
---|---|---|
編譯器 | G++ | MS |
編譯器默認編碼 | UTF8 | UNICODE |
源代碼文件格式 | UTF8 | UTF8 |
輸出結果 | 正常 | 正常 |
幾個有用的函數
/**
* @target : 多字節字符串轉十六進制,支持中英文混合
* @param str : 要轉換成十六進制的字符串
* @param separator : 十六進制字符串間的分隔符
* @return : 16進制字符串
*/
std::string stringToHex(std::string str, std::string separator)
{
const std::string hex = "0123456789ABCDEF";
std::stringstream ss;
for (std::string::size_type i = 0; i<str.size(); ++i)
{
ss << hex[(unsigned char)str[i] >> 4]
<< hex[(unsigned char)str[i] & 0x0f]
<< separator;
}
return ss.str();
}
/**
* @target : Unicode寬字符串轉十六進制,支持中英文混合
* @param str: 要轉換成十六進制的Unicode字符串
* @param separator: 十六進制字符串間的分隔符
* @return : 16進制字符串
*/
std::string wstringToHex(std::wstring str, std::string separator)
{
const std::string hex = "0123456789ABCDEF";
std::stringstream ss;
for (std::wstring::size_type i = 0; i < str.size(); ++i)
{
ss << hex[(uint16_t)str[i] >> 12 & 0x0f]
<< hex[(uint16_t)str[i] >> 8 & 0x0f]
<< separator
<< hex[(uint16_t)str[i] >> 4 & 0x0f]
<< hex[(uint16_t)str[i] & 0x0f]
<< separator;
}
return ss.str();
}
測試
— code blocks —
源代碼
wxCSConv gbkConv(wxFONTENCODING_CP936);
std::string str_gbk(gbkConv.cWX2MB(_T("z國"))); //gbk格式
std::string str_utf8("z國"); // utf8格式
std::wstring str_unicode(gbkConv.cWX2WC(_T("z國")));// unicode格式
std::cout<<"[str_gbk ] = "<<str_gbk<<std::endl;
std::cout<<"[str_utf8 ] = "<<str_utf8<<std::endl;
std::cout<<"[str_unicode ] = "<<str_unicode<<std::endl;
std::cout<<"[str_gbk size ] = "<<str_gbk.size() <<std::endl;
std::cout<<"[str_utf8 size ] = "<<str_utf8.size()<<std::endl;
std::cout<<"[str_unicode size] = "<<str_unicode.size()<<std::endl;
std::cout<<"[str_gbk HEX ] = "<<stringToHex(str_gbk)<<std::endl;
std::cout<<"[str_utf8 HEX ] = "<<stringToHex(str_utf8)<<std::endl;
std::wcout<<"[str_unicode HEX ] = "<<wstringToHex(str_unicode)<<std::endl;
輸出結果
[debug][str_gbk ] = z國
[debug][str_utf8 ] = z鍥nicode ] = z國
[debug][str_gbk size ] = 3
[debug][str_utf8 size ] = 4
[debug][str_unicode size] = 2
[debug][str_gbk HEX ] = 7A B9 FA
[debug][str_utf8 HEX ] = 7A E5 9B BD
[debug][str_unicode HEX ] = 00 7A 56 FD