一張圖看懂字符集和編碼

背景

需要寫一個漢字轉拼音的程序,參考了網上的代碼,沒一個能正常工作的。後來發現是字符集和編碼的鍋,下面做一個總結。

字符編碼的由來

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 

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