前言
阅读前,已默认读者已经熟知
编码,ASCII,字符集等与编码相关的概念,前言中也会对这些概念进行粗略的讲解,若看不懂,可以先去学习相关概念,再来阅读本篇文章。
由于web的普及,各国家之间的信息交换成为可能,然而不同国家对其文字定义的字符集并不相同,这也间接促成了Unicode的诞生。
什么是字符集呢?可以理解成字符与数字(数码)之间的映射集,为了方便,接下来的数字(数码)我都使用16进制进行表示。
比如"中"这个字在GBK字符集中就对应着d6d0这个数字,而在UNICODE字符集中则对应着4e2d这个数字。
但是,单单有字符集并没有完全解决与信息交换以及存储相关的问题,例如,如何将字符集中的字符进行编码并用于计算机间的交换与储存,就是个问题。
举个实际例子吧,已知“中国NO.1”的UNICODE码为4e2d 56fd 004e 004f 002e 0031,若简单的直接依照这个十六进制字符串对其进行保存,那么字符串中英文字符的占用空间将会是ASCII码的两倍!这也太浪费了。
然后,UTF8,一个UNICODE字符集的可变长编码方案诞生了。这里就不展开讨论UTF8的编码原理了,而是直接阐述windows编程中的相关处理办法。
解决方案
UTF8 -> UTF16
由于UTF8的可变长特性,在计算机中我们一般使用byte或char序列来储存U8字符串,在C++中,char序列可以用string代替。可能有疑惑会什么不能用wstring,因为wstring的基本单位是两字节长度的wchar_t,而对于使用UTF8编码后编码长度为三字节的汉字来说,这就可能导致存储相关的问题(字节无法对齐了)。
但这里有个问题啊,windows操作系统默认使用UNICODE的UTF16编码,若在控制台直接输出UTF8字符串,可能得到以下结果
#include <iostream>
using namespace std;
int main()
{
system("title CODEPAGE TEST");
unsigned char str[] = { 0xE4, 0xB8, 0xAD, 0xE5, 0x9B, 0xBD, 0x4E, 0x4F, 0x2E, 0x31, 0x0 };
string s = (char*)str;
printf("%s\n", s.c_str());
system("pause");
return 0;
}
涓�鍥絅O.1


所以我们需要对UTF8编码的string进行转换,转换成UTF16的wstring后再输出就可以正常显示了了
#include <iostream>
#include <locale> // std::wstring_convert
#include <codecvt> // std::codecvt_utf8
using namespace std;
std::wstring UTF8ToUnicode(const std::string& str)
{
std::wstring ret;
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
ret = wcv.from_bytes(str);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return ret;
}
int main()
{
system("title CODEPAGE TEST");
unsigned char str[] = { 0xE4, 0xB8, 0xAD, 0xE5, 0x9B, 0xBD, 0x4E, 0x4F, 0x2E, 0x31, 0x0 };
string s = (char*)str;
wstring wstr = UTF8ToUnicode(s);
setlocale(LC_CTYPE, "");
printf("%ls\n", wstr.c_str());
system("pause");
return 0;
}


由于
vs编译生成的字符串默认使用ANSI(中文电脑上是GBK),而MINGW-W64默认使用UTF8,因此俩输出结果可能有差异。不过观察十六进制输出可以发现,两者除了字节序有差异外,都成功的转换成了
UTF16编码的wstring
UNICODE <-> ANSI
其实在Windows中,string默认的编码是ANSI,中文电脑上ANSI其实是GBK
那么UTF16编码的wstring如何和ANSI(GBK)编码的string进行转换呢?WindowsAPI提供了两个函数WideCharToMultiByte和MultiByteToWideChar。函数名称中的WideChar代表UTF16编码的字符串,而MultiByte代表ANSI编码的字符串
具体实现可以参照以下代码
因为要用到winAPI,所以需要引用windows.h
std::wstring CharToWchar(string s, size_t m_encode = CP_ACP)
{
std::wstring ret;
const char* c = s.c_str();
int len = MultiByteToWideChar(m_encode, 0, c, strlen(c), NULL, 0);
wchar_t* m_wchar = new wchar_t[len + 1];
MultiByteToWideChar(m_encode, 0, c, strlen(c), m_wchar, len);
m_wchar[len] = '\0';
ret = m_wchar;
delete[] m_wchar;
return ret;
}
std::string WcharToChar(wstring ws, size_t m_encode= CP_ACP)
{
std::string ret;
const wchar_t* wp = ws.c_str();
int len = WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), NULL, 0, NULL, NULL);
char* m_char = new char[len + 1];
WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), m_char, len, NULL, NULL);
m_char[len] = '\0';
ret = m_char;
delete[] m_char;
return ret;
}





真不错
嘿嘿୧(๑•̀⌄•́๑)૭
好厉害鸭
厉害👍🏻
写这篇博文的起因是一位群友提了一个wstring为什么无法正确输出的问题
你在手机上用chrome?
其实是手机版的Edge啦