一张图看懂字符集和编码

背景

需要写一个汉字转拼音的程序,参考了网上的代码,没一个能正常工作的。后来发现是字符集和编码的锅,下面做一个总结。

字符编码的由来

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 

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