在介绍如何使用C++11标准库进行中文编码转换之前,先说一下byte string、multibyte string、wide string之间的区别。

byte string

由8比特的字节组成的字符串。由char表示字节。因而字符串长度=字节数=char数

multibyte string

在内存布局上与byte string相同。但是由于它是区域(locale)相关的,所以它有可能表示的是UTF-8, GB18030, EUC-JP, Shift-JIS等格式的字符串,而这些格式中的每个字码(codepoint)可能是由多个连续的char组合构成的,所以字符串长度=字码数,但!=char数,也!=字节数。

 wide string

每个宽字符由于操作系统的不同,其宽度为16位或32位的多语言字符代码。格式分别采用unicode(UTF-16LE)、UTF-16、UTF-32,由wchar_t, char16_t, char32_t表示。

C++标准库中对应关系:

char *        std::string
wchar_t*   std::wstring
char16_t*  std::u16string
char32_t*  std::u32string

1.string与wstring互相转化

C++标准库从C++11开始提供了std::codecvt_utf8和std::codecvt_byname两个转换器来完成编码转换,可使用的通用代码如下:

#include <string>
#include <locale>
#include <codecvt>// string的编码方式为utf8,则采用:
std::string wstring2utf8string(const std::wstring& str)
{static std::wstring_convert<std::codecvt_utf8<wchar_t> > strCnv;return strCnv.to_bytes(str);
}std::wstring utf8string2wstring(const std::string& str)
{static std::wstring_convert< std::codecvt_utf8<wchar_t> > strCnv;return strCnv.from_bytes(str);
}// string的编码方式为除utf8外的其它编码方式,可采用:
std::string wstring2string(const std::wstring& str, const std::string& locale)
{typedef std::codecvt_byname<wchar_t, char, std::mbstate_t> F;static std::wstring_convert<F> strCnv(new F(locale));return strCnv.to_bytes(str);
}std::wstring string2wstring(const std::string& str, const std::string& locale)
{typedef std::codecvt_byname<wchar_t, char, std::mbstate_t> F;static std::wstring_convert<F> strCnv(new F(locale));return strCnv.from_bytes(str);
}

如果是GBK string与wstring互相转化,locale可取值:

linux下:

zh_CN.GBK
zh_CN.GB2312
zh_CN.GB18030

windows下:

标准格式的locale:
Chinese_China.936
zh-CN
.936
非标准格式的locale:
chs
Chinese-simplified
Chinese
ZHI
不能使用的locale:
 Chinese.936,chs.936,Chinese.GB2312,chs.GB18030等此类值。

下面通过一些例子说明上面的函数如何使用吧。

2.string与wstring如何输出到控制台

代码页为936

以在windows控制台为例,举例说明:

// testCode.cpp
#include <string>
#include <iostream>
#include <fstream>
#include <codecvt>
#include <locale>int main() {std::wstring txt = L"中国人";std::wcout << txt << endl;
}

程序编译后在中文版windows中运行结果:

控制台使用的代码页为936(也就是GBK编码),输出结果为乱码。因为txt是unicode,而控制台是GBK编码,乱码是由于没有做编码转换造成的。

修改代码,添加一个转换器:

int main() {std::wstring txt = L"中国人";wcout.imbue(std::locale(std::locale("Chinese"), new std::codecvt_byname<wchar_t, char, std::mbstate_t>("Chinese")));  // wcout.imbue(std::locale("Chinese")); // 也可以简写成这种形式,其中默认带了std::codecvt_byname<wchar_t, char, std::mbstate_t>("Chinese")转换器std::wcout << txt << endl;
}

此时,输出结果就正常了。

如果修改代码页为65001(也就是UTF-8编码),再执行如下代码:

int main() {std::wstring txt = L"中国人";std::wcout << txt << endl;
}

会发现没有结果输出,说明从unicode到utf-8没有转换成功。

修改代码,添加一个转换器:

int main() {std::wstring txt = L"中国人";wcout.imbue(std::locale(std::locale("Chinese"), new std::codecvt_utf8<wchar_t>()));std::wcout << txt << endl;
}

此时,结果正确输出了:

由于wstring是unicode,它转换到其它编码格式,只需要使用一次转换器就可以了,但如果使用的是string,又该怎样做转换?比如使用如下代码:

int main() {std::string txt = u8"中国人";std::cout << txt << endl;
}

在代码页为936的情况下,执行输出为乱码:

修改代码,添加两个转换器:

int main() {std::string txt = u8"中国人";   // string的编码格式为utf-8std::wstring wtxt = utf8string2wstring(txt);    // 将utf-8的string转换为wstringstd::string txt_gbk = wstring2string(wtxt, "Chinese");    // 再将wstring转换为gbk的stringstd::cout << txt_gbk << endl;
}

此时,输出结果正常:

代码页为65001

再修改一下代码:

int main() {std::string txt = "中国人";std::cout << txt << endl;
}

在代码页为65001的情况下,执行无输出,说明执行结果失败:

添加两个转换方法再试试:

int main() {std::string txt = "中国人";std::wstring wtxt = string2wstring(txt, "Chinese");std::string txt_uft8 = wstring2utf8string(wtxt);std::cout << txt_uft8 << endl;
}

在代码页为65001的情况下,执行成功:

使用std::wcout试一下:

int main() {std::string txt = "中国人";std::wstring wtxt = string2wstring(txt, "Chinese");wcout.imbue(std::locale(std::locale("Chinese"), new std::codecvt_utf8<wchar_t>()));std::wcout << wtxt << endl;
}

在代码页为65001的情况下,执行结果正常:

再举一个保存到文件的例子:

int main() {std::string txt = "中国人";std::wstring wtxt = string2wstring(txt, "Chinese");std::string txt_uft8 = wstring2utf8string(wtxt);std::ofstream of("D:/temp/text.txt");of << txt_uft8 << endl;
}

以上代码执行成功,并将字符串以utf-8保存到文件了。

再使用如下代码试试:

int main() {std::string txt = u8"中国人";std::ofstream of("D:/temp/text.txt");of << txt << endl;
}

也能以utf-8保存到文件。

3.在输入输出流中使用编码转换

除了直接使用转换器做字符串之间的转换外,如果用到的输入与输出流,则可以直接在输入输出流上配置需要的转换器。上面已经有多个例子了,这里再补充一个例子,代码如下:

#include <iostream>
#include <fstream>
#include <string>
#include <locale>int main()
{// text.txt是UTF-8编码的文件std::wifstream fin("D:/temp/text.txt");// 文件输入流中使用UTF-8转换器fin.imbue(std::locale(std::locale("zh-CN"), new std::codecvt_utf8<wchar_t>()));// 控制台输出流中使用GBK转换器std::wcout.imbue(std::locale(std::locale("zh-CN"), new std::codecvt_byname<wchar_t, char, std::mbstate_t>("Chinese_China.936")));for (wchar_t c; fin.get(c); ) {std::wcout  << c << endl;}
}

以上是在windows上可执行的代码 ,如果要在Linux上运行,只需要把Chinese_China.936替换成zh_CN.GBK即可。

4.char* 与wchar_t*互相转化

在C++11之前,C++标准库中提供了如下两个函数进行编码转换。需要说明的是,这两个函数在windows下不支持UTF-8,而在Linux下是可以支持UTF-8的:

#include <cstdlib>std::size_t mbstowcs( wchar_t* dst, const char* src, std::size_t len);
std::size_t wcstombs( char* dst, const wchar_t* src, std::size_t len);

windows不支持UTF-8的原因,据说是因为这两个函数一开始只支持ANSI内的多字节编码方式,而ANSI内的多字节编码方式的特点是每个字符不超过两个字节,后来utf-8出现后,由于UTF-8中的字符是有可能超过两个字节的,如果要加入UTF-8,会对现在函数做大量修改,因此Microsoft没有让这两个函数支持UTF-8。也可以说,这两个函数不认为UTF-8是多字节编码方式。

以下为Linux下可以运行的代码:

#include <iostream>
#include <clocale>
#include <cstdlib>
int main()
{std::setlocale(LC_ALL, "en_US.utf8");std::wcout.imbue(std::locale("en_US.utf8"));const char* mbstr = u8"中国人";wchar_t wstr[5];std::mbstowcs(wstr, mbstr, 5);std::wcout << "wide string: " << wstr << '\n';
}
#include <iostream>
#include <clocale>
#include <cstdlib>int main()
{std::setlocale(LC_ALL, "en_US.utf8");// UTF-8 narrow multibyte encodingconst wchar_t* wstr = L"中国人";char mbstr[11];std::wcstombs(mbstr, wstr, 11);std::cout << "multibyte string: " << mbstr << '\n';
}

如果要在windows上使用与UTF-8相关的编码转换,也可以考虑使用如下函数,只不过这将失去平台移植性:

MultiByteToWideChar
WideCharToMultiByte
void mbtowchar(const char* input, wchar_t* output) {int len = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);MultiByteToWideChar(CP_UTF8, 0, input, -1, output, len);
}

总结

从C++11开始,标准C++库引入相关API,提供了标准的字符编码转换方式,方便开发者开发跨平台的字符编码转换代码。然而,上述代码中使用的wstring_convert、codecvt_utf8已在C++17被弃用了,至于由什么来替代,C++标准中没有说,现在只好继续使用它们,等待新的C++标准出来了。

参考文档

String and Character Literals (C++)
Locale Names, Languages, and Country/Region Strings
Locale Names
Code Page
Language Strings
Country/Region Strings
char, wchar_t, char16_t, char32_t
MultiByteToWideChar function
WideCharToMultiByte function

Unicode part 1: Windows console i/o approaches
Unicode part 2: UTF-8 stream mode

Unicode转UTF-16
UTF-8 Everywhere
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets

setlocale()

The Complete Guide to C++ Strings, Part I - Win32 Character Encodings
The Complete Guide to C++ Strings, Part II - String Wrapper Classes

C++:中文编码转换相关推荐

  1. 一网打尽中文编码转换---6种编码30个方向的转换

    一网打尽中文编码转换--6种编码30个方向的转换 1.问题提出 在学编程序时,曾经有人问过"你可以编一个记事本程序吗?"当时很不屑一顾,但是随着学习MFC的深入,了解到记事本程序也 ...

  2. [运维]PowerShell简体中文编码转换

    [运维笔记]PowerShell简体中文编码转换 本文地址: https://blog.csdn.net/qq_28550263/article/details/121310472 以下这个函数用于将 ...

  3. mfc 中文乱码转换为正常中文_中文编码转换

    中文编码转换--6种编码30个方向的转换 1.问题提出 在学编程序时,曾经有人问过"你可以编一个记事本程序吗?"当时很不屑一顾,但是随着学习MFC的深入,了解到记事本程序也并非易事 ...

  4. python3中文编码转换显示

    在python3中: print(info.toJsonStr()) 结果: {"pnt_name": "698\u58a9-z\u8f74\u503e\u89d2-16 ...

  5. java unicode 转换_Java unicode中文编码转换和反转

    在java的很多配置文件中,尤其是国际化资源中经常遇到类似\uf432这样的unicode编码,搜集了下该编码相关的资料,大致处理方法有如下: 1.Unicode转 汉字字符串. 这个过程最简单的方式 ...

  6. Java中文编码转换与字节长度判断

    引言 与第三方通信的过程中,往往涉及到报文中包含中文的情况,此时的报文长度将由于编码的类型不同产生变化,此时需要通过根据双方接口要求进行中文的转码. 方法 主要是使用String类型中的.getByt ...

  7. linux 繁体中文转为简体,Linux下对文件进行编码转换(简体→繁体,繁体→简体,简体繁体→UTF-8)...

    其实说到编码转换,那就不得不说到iconv这个库了,如果已经安装了这个工具,那在命令行下直接打这个命令就行了.下面就用这个工具来实现中文系统下的常用编码转换. 首先,来了解一下iconv这个命令的使用 ...

  8. 中文字符串的编码转换 UTF-8//GBK

    golang在处理中文时默认的是utf-8编码,当某些情况下遇到GBK编码或需要GBK编码时,就会出现显示乱码的问题. 1. simplifiedchinese golang官方有针对中文编码转换的包 ...

  9. 繁简转换OpenCC,autogb 和 autob5,iconv,python的jianfan包

    OpenCC OpenCC 是跨平台.多语言的开放中文转换库,除了基本的简繁转换功能外,用户还可以选择对不同用词习惯和异体字的处理方式. OpenCC 还提供方便的网页转换界面. OpenOffice ...

最新文章

  1. 往事并不如烟@武汉 --- 和同事们一起'诈金花’
  2. shell脚本--字符串处理和动态数组
  3. Can't create handler inside thread that has not called Looper.prepare() 解决办法
  4. Qt控件大小自适应电脑分辨率问题
  5. 判断设置两天后时间,时间戳
  6. PHP ob_get_level嵌套输出缓冲
  7. 记录拷贝:centos安装jdk
  8. Java 25天基础-DAY 05-面向对象-构造函数
  9. python之列表相关操作
  10. 【STC15】使用PCA0和PCA1做的模拟串口
  11. USGS官网批量下载卫星数据方法
  12. 一招解决 Mac JD-JUI 打不开问题
  13. 制造行业实施作业成本法案例(AMT 邓为民)
  14. php开发桌面应用程序_使用PHP开发跨平台桌面应用程序的3种方法
  15. [转]人生就像一张茶几,摆满了各种杯具洗具餐具
  16. 【Vue 基础知识】keep-alive是什么?怎么用?
  17. 求余数联系和赋值运算
  18. 迅视资管 华为哈勃入股中蓝电子,持股7.83%
  19. 旅游指南之五----途登山装备清单
  20. 为了帮助贝索斯离婚,我总结了这些APP……

热门文章

  1. [iOS]贝聊 IAP 实战之见坑填坑
  2. 从Oracle到MySQL,余额宝的云实践
  3. aspose.words更新目录
  4. 【考研复试】遥感主要知识点汇总
  5. 浅谈辄止WCF:完成最基本的WCF项目(1)
  6. word 当前页插入页眉、页脚、页码。要求奇偶页眉不同,而页码连续。
  7. 使用Tableau Prep还是Desktop进行数据准备?答:为什么不两者兼而有之?
  8. 【Matlab三维视图】 在界面内绘制下面的二维函数所表示的曲面
  9. 医院计算机管理工资,揭秘!三甲医院与二甲医院的薪水区别!
  10. ZEMAX的激光扩束镜的设计优化