char:一个字节8bit表示,最多表示256个字符,表示和用来处理ASCII字符集,国际通用
wchar_t:多字节字符表示,典型2个字节或者4个字节,如GNU libc中为4B,可以表示更多的字符,满足国际化应用开发的需求,实现标准 在开发中ASCII编码字符都是用char来表示,可以转换成wchar_t表示;wchar_t类型与Unicode编码是完全独立的概念,不过在实现上Unicode编码一般用wchar_t来表示实现而已,但wchar_t字符并不一定就是Unicode编码字符。
string: char字符列表或者是字节列表(bytes)
wstring: wchar_t字符列表或者是宽子节列表
对应两种字符类型的输出函数流对象有:
sprintf/wsprintf: 分别对应char与wchar_t
cout/wcout:分别对应string与wstring
stringstream/wstringstream: 分别对应string与wstring
C++98标准中一些表示字符串常量的标识有:
“您好”: string字符串常量(字节数组),使用当前系统代码页进行编码
C++11标准中增加了一些表示字符串常量的标识,如下有:
L“您好!”: wstring字符串常量,使用文件保存编码方式字符集
R“(您好 \n)”: 原始字符串常量(字节数组),保留所有的字符
u8“您好!”: string字符串常量(字节数组),使用UTF8进行编码保存
一般一个字符集等同于一个编码方式,ANSI体系的字符集如ASCII、ISO 8859-1、GB2312、GBK等等都是如此。一般我们说一种编码都是针对某一特定的字符集。
一个字符集上也可以有多种编码方式,例如UCS字符集(也是Unicode使用的字符集)上有UTF-8、UTF-16、UTF-32等编码方式。
已知有很多的字符集,比如:ASCII,UTF8,GBK,GB2312,UTF16,UTF32,UNICODE,Latin等等。常用中文字符集编码有:
UTF8:又分为带签名和不带签名两种,Windows代码页为65001,VS中应该选择【UTF8-带签名】的格式
GBK/GB2312:Windows代码页为936
GB18030: Windows代码页为54936
小技巧:修改Windows系统中cmd命令行窗口的显示字符集,默认字符集为OS字符集,如GBK-936。如果希望显示UTF8字符,则可以修改:chcp 65001.
在VS项目的调试命令窗口中无法手动修改,可以通过system函数来修改:
system("C:\\Windows\\system32\\chcp 65001");// 修改终端字符集为UTF8
可以将源文件保存成不同的编码方式,如果文件中有中文,则必须选择可以对中文进行编码的字符集,如UTF8,GBK,GB2312等,否认可能会出现莫名其妙的编译错误,因为文件中存在无法识别的字符内容。
在Visual Studio中,选中文件后该设置在:【文件 - 高级保存选项】中。
在处理中文时,不同的应用场景下总是无法避免进行GBK和UTF8之间的相互转换,C++11标识提供了<locale>和<codecvt>机制和工具来简化处理。
借助其中的std::wstring_convert和std::codecvt_utf8模板,通过wchar_t类型为中介,可以快速地实现转换,基本代码如下:
/* 转换GBK编码的中文到UTF8编码:GBK - WChar - UTF8两次转换 */ //GBK在linux下的locale名可能是"zh_CN.GBK" const char* GBK_LOCALE_NAME = ".936"; //GBK在windows下的locale name std::wstring_convert<std::codecvt_byname<wchar_t, char, mbstate_t>> Conver_GBK(new codecvt_byname<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME)); //GBK - wchar_t std::wstring _wname = Conver_GBK.from_bytes(data.Name); // 输入为char*的字符串表示或者数组,输出为wstring std::wstring _waddr = Conver_GBK.from_bytes(data.Address);// 输入为char*的字符串表示或者数组,输出为wstring std::wstring _wdept = Conver_GBK.from_bytes(data.GrantDept);// 输入为char*的字符串表示或者数组,输出为wstring wcout << "Name: " << _wname << ",addr:" << _waddr << ",dept:" << _wdept << endl; // 将wstring转化为UTF8编码的字节数组string表示 std::wstring_convert<std::codecvt_utf8<wchar_t>> conv; string _name = conv.to_bytes(_wname);// 输入为wstring字符串 string _addr = conv.to_bytes(_waddr); string _dept = conv.to_bytes(_wdept); // 将UTF8字符串转换为GBK字符编码表示string字节数组 std::string _name = Conver_GBK.to_bytes(conv.from_bytes(_name)); // 先转换成wstring,然后再转换成GBK的string std::string _addr = Conver_GBK.to_bytes(conv.from_bytes(_addr)); // 先转换成wstring,然后再转换成GBK的string
代码页是字符集编码的别名,也有人称“内码表”。下面列出了部分代码页及其国家(地区)或者语言:
代码页 国家(地区)或语言
437 美国
932 日文(Shift-JIS)
936 中国 - 简体中文(GB2312)
950 繁体中文(Big5)
1200 Unicode
1201 Unicode (Big-Endian)
50220 日文(JIS)
50225 韩文(ISO)
50932 日文(自动选择)
50949 韩文(自动选择)
52936 简体中文(HZ)
65000 Unicode (UTF-7)
65001 Unicode (UTF-8)
char16_t* p1 = u"中国";//把字符串初始化为UTF16字符串存储 char32_t* p2 = U"中国";//把字符串初始化为UTF32字符串存储 wchar_t* p3 = L"中国";//win是UCS2码下等同UTF16字符串,Linxu是UCS4码下等同utf32字符串 char* p4 = u8"中国";//把字符串初始化为UTF8字符串存储 char* p5 = "中国";//p5字符的编码格式见我的上篇博文《c++字符串乱码研究》 std::string str1 = u8"中国"; std::wstring str2 = L"中国"; std::u16String str3 = u"中国"; std::u32String str4 = U"中国";
//std::string 转为 std::wstring( utf-8 --> wchar ) //src必须是UTF8否则抛异常 std::wstring utf8_to_wstr(const std::string& src) { std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; return converter.from_bytes(src); } //std::wstring转为std::string(wchar --> utf-8) std::string wstr_to_utf8(const std::wstring& src) { std::wstring_convert<std::codecvt_utf8<wchar_t>> convert; return convert.to_bytes(src); }
在构造std::wstring_convert对象的时候需要传入一个codecvt对象的指针,如果没有传入,则默认使用new codecvt来创建。std::wstring_convert自行维护codecvt对象的生命周期,它的析构函数会调用delete操作符来删除该对象。这就限制了只能使用通过new操作符来创建的codecvt,而不能使用从std::locale中获取的codecvt。
在C++标准提供的codecvt中,能够直接用于std::wstring_convert的只有三个:std::codecvt_utf8,std::codecvt_utf16以及std::codecvt_utf8_utf16。可见,标准只支持UTF族的字符编码。为了获取其它字符编码的codecvt,需要使std::codecvt_byname,这个类可以通过字符编码的名称来创建一个codecvt。这看起来挺不错,但遗憾的是,字符编码的名称并没有统一的标准,各个平台的支持情况都不一样。例如,在Windows下可以使用“chs”或“.936”来创建简体中文编码的codecvt,linux下可是“zh_CN.GBK”在Mac OS X下则要使用“zh_cn.gb2312”;甚至在Mac OS X下,即使成功创建了这个codecvt,它也不能正常地转换。
由于历史原因,std::codecvt_byname的析构函数是protected的(g++是,VC好像不是),std::wstring_convert不能对它调用delete,所以首先要自行定义一个类来继承std::codecvt_byname:
class chs_codecvt : public std::codecvt_byname<wchar_t, char, std::mbstate_t> { public: chs_codecvt() : codecvt_byname("chs") {} //zh_CN.GBK or .936 }; // std::wstring 转 GBK(wchar --> ansi) std::string wstr_to_gbk(const std::wstring& str) { std::wstring_convert<chs_codecvt> converter; return converter.to_bytes(str); } //GBK 转为 std::wstring( ansi --> wchar ) std::wstring gbk_to_wstr(const std::string& str) { std::wstring_convert<chs_codecvt> converter; return converter.from_bytes(str); }
#include<string> #include<windows.h> #include<vector> using namespace std; //utf8 转 Unicode std::wstring Utf82Unicode(const std::string& utf8string) { int widesize = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, NULL, 0); if (widesize == ERROR_NO_UNICODE_TRANSLATION) { throw std::exception("Invalid UTF-8 sequence."); } if (widesize == 0) { throw std::exception("Error in conversion."); } std::vector<wchar_t> resultstring(widesize); int convresult = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, &resultstring[0], widesize); if (convresult != widesize) { throw std::exception("La falla!"); } return std::wstring(&resultstring[0]); } //unicode 转为 ascii std::string WideByte2Acsi(std::wstring& wstrcode) { int asciisize = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, NULL, 0, NULL, NULL); if (asciisize == ERROR_NO_UNICODE_TRANSLATION) { throw std::exception("Invalid UTF-8 sequence."); } if (asciisize == 0) { throw std::exception("Error in conversion."); } std::vector<char> resultstring(asciisize); int convresult =::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, &resultstring[0], asciisize, NULL, NULL); if (convresult != asciisize) { throw std::exception("La falla!"); } return std::string(&resultstring[0]); } //utf-8 转 ascii std::string UTF_82ASCII(std::string& strUtf8Code) { std::string strRet(""); //先把 utf8 转为 unicode std::wstring wstr = Utf82Unicode(strUtf8Code); //最后把 unicode 转为 ascii strRet = WideByte2Acsi(wstr); return strRet; } //ascii 转 Unicode std::wstring Acsi2WideByte(std::string& strascii) { int widesize = MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1, NULL, 0); if (widesize == ERROR_NO_UNICODE_TRANSLATION) { throw std::exception("Invalid UTF-8 sequence."); } if (widesize == 0) { throw std::exception("Error in conversion."); } std::vector<wchar_t> resultstring(widesize); int convresult = MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1, &resultstring[0], widesize); if (convresult != widesize) { throw std::exception("La falla!"); } return std::wstring(&resultstring[0]); } //Unicode 转 Utf8 std::string Unicode2Utf8(const std::wstring& widestring) { int utf8size = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, NULL, 0, NULL, NULL); if (utf8size == 0) { throw std::exception("Error in conversion."); } std::vector<char> resultstring(utf8size); int convresult = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, &resultstring[0], utf8size, NULL, NULL); if (convresult != utf8size) { throw std::exception("La falla!"); } return std::string(&resultstring[0]); } //ascii 转 Utf8 std::string ASCII2UTF_8(std::string& strAsciiCode) { std::string strRet(""); //先把 ascii 转为 unicode std::wstring wstr = Acsi2WideByte(strAsciiCode); //最后把 unicode 转为 utf8 strRet = Unicode2Utf8(wstr); return strRet; }
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 => wstring std::wstring_convert<std::codecvt_utf8<wchar_t>> convert; std::string utf8str = convert.to_bytes(wString); // wstring => utf-8 return utf8str; } 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 => wstring std::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 => string return str; }