前言
阅读前,已默认读者已经熟知
编码
,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啦