背景
需要写一个汉字转拼音的程序,参考了网上的代码,没一个能正常工作的。后来发现是字符集和编码的锅,下面做一个总结。
字符编码的由来
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