本文用于介绍不同编码格式的string(char*)之间的转换。明确Unicode,UTF-8,string,wstring概念,以及locale name之前, 先简单了解两个概念

  • 字符集: 为每一个字符(asic,中文,日文,俄文等)分配一个唯一的ID(又称码位)。
  • 编码规则:将码位转换为字节序列的规则(编码/解码的过程)

由于UTF-8使用广泛,以utf-8编码为例,介绍其与其它编码方式的流程。

UTF-8

广义的Unicode的一个标准,定义了一个字符集以及一系列的编码规则,即 Unicode 字符集和 UTF-8、UTF-16、UTF-32 等等编码……

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符。会将一个码位编码为 1 到 4 个字节(理论最多6个字节):

  • 1字节 一个US-ASCIl字符(Unicode范围由U+0000~U+007F)。
  • 2字节 带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母(Unicode范围由U+0080~U+07FF)。
  • 3字节 其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
  • 4字节 其他极少使用的语言字符使用4字节编码。
Unicode/UCS-4 bit数 UTF-8 byte数 备注
0000 ~ 007F 0~7 0XXX XXXX 1 ASIC 码
0080 ~ 07FF 8~11 110XXXXX 10XXXXXX 2
0800 ~ FFFF 12~16 1110 XXXX 10XXXXXX 10XXXXXX 3 以上基本定义范围:0~FFFF
1 0000 ~ 1F FFFF 17~21 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX 4 Unicode6.1定义范围:0~10 FFFF

上表是Unicode中任意字符使用utf-8编码的规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。

例如「知」的码位是 30693,记作 U+77E5(30693 的十六进制为 0x77E5),其对应的UTF-8 编码为字节序列 E79FA5。

根据上表中的编码规则,之前的「知」字的码位 U+77E5 属于第三行的范围,使用三个3个字节编码,将0x77E5的二进制位从地位开始放入编码模板的

       7    7    E    5    0111 0111 1110 0101    二进制的 77E5
--------------------------0111   011111   100101 二进制的 77E5
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行)
11100111 10011111 10100101 代入模版E   7    9   F    A   5

这就是将 U+77E5 按照 UTF-8 编码为字节序列 E79FA5 的过程。反之亦然。

判断字符串是否为utf-8编码

字符串可能存储在std::string或者char*中,前者实际是 char 内存的一个封装,后续说明全部以std::string为例。

了解utf-8编码规则,首先可以根据每个字节的最高比特位判断其为单字节多字节编码,若为0,则可能是单字节,还需继续判断。 若为1,则需要查看当前是几个字节,且后续字节的前两位必须是10。直到所有字节判断结束。

bool IsTextUTF8(const std::string& str)
{char nBytes=0;//UFT8可用1-6个字节编码,ASCII用一个字节unsigned char chr;bool bAllAscii = true; //如果全部都是ASCII, 说明不是UTF-8for(int i=0; i < str.length();i++){chr = str[i];// 判断是否ASCII编码,如果不是,说明有可能是UTF-8,ASCII用7位编码,// 但用一个字节存,最高位标记为0,o0xxxxxxxif( (chr&0x80) != 0 )bAllAscii= false;if(nBytes==0) //如果不是ASCII码,应该是多字节符,计算字节数{if(chr>=0x80){if(chr>=0xFC&&chr<=0xFD)   nBytes=6;else if(chr>=0xF8)         nBytes=5;else if(chr>=0xF0)         nBytes=4;else if(chr>=0xE0)         nBytes=3;else if(chr>=0xC0)         nBytes=2;else{return false;}nBytes--;}}else //多字节符的非首字节,应为 10xxxxxx{if( (chr&0xC0) != 0x80 ){return false;}nBytes--;}}if( nBytes > 0 ) //违返规则return false;if( bAllAscii ) //如果全部都是ASCII, 说明不是UTF-8return false;return true;
}

不同编码的std::string转换

c/c++修改字符集locale name可能会影响系统正在运行的有关字符编解码的程序,所以使用C++11的std::wstring_convert配合std::codecvt模板类。

  • std::codecvt:编码转换特性类,用在wstring_convert的模板参数中来指定使用哪种编码。
  • std::wstring_convert:转码器,接收一个类似codecvt描述编码转换特性的模板参数,用于将本地化的宽字符wstring和指定编码的字节化string进行互转。

所以编码A和B互转的实现方式就是:借助本地化宽字符串,先将以A编码的string转为本地化的wstring,再将本地化的wstring转为B编码后的string。

示例一:

这里给一个windows下,GBK string转UTF8 string的例子:
首先将GBK string转wstring

const char* GBK_LOCALE_NAME = ".936"; //GBK在windows下的locale name
std::string gbk_str {"\xCC\xCC"};  //0xCCCC,"烫"的GBK码//构造GBK与wstring间的转码器(wstring_convert在析构时会负责销毁codecvt,
// 所以不用自己delete)
std::wstring_convert<codecvt_byname<wchar_t, char, mbstate_t>>
// 或 std::wstring_convert<std::codecvt<wchar_t, char, mbstate_t>>cv1(new std::codecvt<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));
wstring tmp_wstr = cv1.from_bytes(gbk_str);

再将wstring转为UTF8 string

std::wstring_convert<codecvt_utf8<wchar_t>> cv2;
std::string utf8_str = cv.to_bytes(tmp_wstr);

转码就完成了。utf8_str里的内容应该是"\xE7\x83\xAB"(烫的UTF8)。

转换的完整代码

std::string StringToUTF8(const std::string& gbkData)
{const char* GBK_LOCALE_NAME = "CHS";  //GBK在windows下的locale name(.936, CHS ), linux下的locale名可能是"zh_CN.GBK"std::wstring_convert<std::codecvt<wchar_t, char, mbstate_t>>conv(new std::codecvt<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));std::wstring wString = conv.from_bytes(gbkData);    // string => wstringstd::wstring_convert<std::codecvt_utf8<wchar_t>> convert;std::string utf8str = convert.to_bytes(wString);     // wstring => utf-8return utf8str;
}

示例二:

例如在window下,将utf8编码转换为CHS编码的代码为

std::string UTF8ToString(const std::string& utf8Data)
{std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;std::wstring wString = conv.from_bytes(utf8Data);    // utf-8 => wstringstd::wstring_convert<std::codecvt< wchar_t, char, std::mbstate_t>>convert(new std::codecvt< wchar_t, char, std::mbstate_t>("CHS"));std::string str = convert.to_bytes(wString);     // wstring => stringreturn str;
}

另外:

由于编码的locale name是操作系统决定的(例如GBK在linux下的locale名可能是"zh_CN.GBK",而windows下是".936"),因此做跨平台的话仍然要给不同的系统做适配。

项目中可以使用iconv开源库。

c++ string/char* 字符编码转换 (utf8,chs,gbk...)相关推荐

  1. Qt 字符编码转换(UTF-8 转换为 GBK) \u7528\u6237\u672a\u7b7e\u7ea6

    UTF-8 转换为 GBK \u7528\u6237\u672a\u7b7e\u7ea6 编码知识(\u7528\u6237\等的就是Unicode编码) 转换编码示例 编码知识(\u7528\u62 ...

  2. Linux字符编码转换 UTF8转GB3212

    在LINUX上进行编码转换时,既可以利用iconv函数族编程实现,也可以利用iconv命令来实现,只不过后者是针对文件的,即将指定文件从一种编码转换为另一种编码.     一.利用iconv函数族进行 ...

  3. C语言判断读取的文件内容字符编码是UTF-8还是GBK

    自定义两个字符编码判断函数 bool is_str_utf8(const char* str); bool is_str_gbk(const char* str); 测试文件 代码详细: #inclu ...

  4. Android字符编码转换,GBK转UTF-8

    Android字符编码转换,GBK转UTF-8 网上看了很多都不能用,最后看到这个方法,很靠谱,分享给大家! String str; str = new String(str.getBytes(&qu ...

  5. Qt中的字符编码转换:UTF8、Unicode、GBK、ASCII、16进制字符、16进制数值

    文章目录 前言 简述 ASCII GBK Unicode UTF-8 应用场景 开发环境 编码转换 16进制数值转换为16进制字符 16进制数值转化为字符串 16进制字符串转换为Unicode字符串 ...

  6. 字符编码转换_进制转换(GB2312,GBK,JNI,HexTOStr)

    [cpp] view plain copy print? // /* ASCII 英文一个字节 gb2312,gbk 中文两个字节,英文一个字节 在中文系统中ansi一般指gb2312或gbk GB2 ...

  7. c语言使用iconv函数实现字符编码转换

    c语言使用iconv函数实现字符编码转换 linux下提供了iconv库来实现字符编码转换,先介绍下命令行: iconv [-f encoding] [-t encoding] [inputfile ...

  8. linux windows 字符编码转换,Linux操作系统下汉字编码的转换

    因为项目的需要linux下将GBK编码转换为utf8编码,google一下,网上的相关资源比较少,下面的操作经过本人的反复试验.本例子同样适用于其他的编码转换. 有gbk到utf8的转换过程,需要经过 ...

  9. java utf8转iso8859-1_Java字符编码处理(UTF-8/ISO-8859-1)之一 –读文本文件乱码问题 | 学步园...

    Java字符编码处理(UTF-8/ISO-8859-1) 之一  -- 读文本文件乱码问题 当我们用java.io.Properties的load()方法读属性文件,一般会将字符编码成ISO-8859 ...

最新文章

  1. C#复制数组的两种方式,以及效率比较
  2. WCF4.0新特性体验(6):路由服务Routing Service(下)
  3. 我从大厂面试中学到的关于 C# 的知识
  4. Android之添加固定图标到桌面
  5. 音视频技术(25)---MPEG-2、MPEG-4、H.264 与视频带宽
  6. bootsect Linux,linux_bootsect选读.doc
  7. 基于法律裁判文书的法律判决大数据预测
  8. android qq skype,蓝牙耳机与IVT组合实现在电脑聊天、打电话(QQ、Skype)
  9. GIS空间分析之Clip
  10. 智能化的Conversational UI是移动发展的一个趋势
  11. 2021年中国DDI(DNS-DHCP-IPAM)解决方案市场趋势报告、技术动态创新及2027年市场预测
  12. Altium Designer PCB封装库放置3D模型对齐问题的解决思路
  13. WeiPHP5.0,公众号与小程序结合的最佳开发框架
  14. 微信分享域名防屏蔽 防微信拦截网址系统
  15. 楼宇系统服务器,【产品介绍】楼宇管理平台
  16. 思维导图---“计算机硬件系统”练手
  17. Python 之体重指数( BMI )
  18. 西北农林科技大学计算机学院保研,坐落在小镇上的985大学,学生家长纷纷嫌弃,考上的人却在偷笑...
  19. 【ARM】ARM处理器概述
  20. JS 获取网页源代码

热门文章

  1. 学计算机的孩子很可爱的
  2. JavaScript基础测试
  3. 如何在 JavaScript 中使字符串的第一个字母大写?
  4. java的取整_Java取整方法总结
  5. 使用pdf.js展示pdf文件(亲测可用)
  6. Win10:添加或者删除开机启动项,在开机启动项中添加在用户自定义的启动文件
  7. 【django】django中使用jinja2模板
  8. 光绘文件输出GERBER文件
  9. 什么是库函数、寄存器?如何新建一个库函数的工程模板?(第四天,有检讨)
  10. Eclipse中文乱码解决方法