文章摘要
前言 阅读前,已默认读者已经熟知 编码 , ASCII , 字符集 等与 编码 相关的概念,前言中也会对这些概念进行粗略的讲解,若看不懂,可以先去学习相关概念,再来阅读本篇文章。 由于web的普及,各国家之间的信息交换成为可能,然而不同国家对其文字定义的 字符集 并不相同,这也间接促成了 Unicode 的诞生。 什么是 字符集 呢?可以理解成 字符 与 数字 (数码)之间的 映射集 ,为了方便,…
前言 阅读前,已默认读者已经熟知 编码 , 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("%sn", s.c_str()); system("pause"); return 0; } 涓�鍥絅O.1 VS2022运行结果 MINGW-W64运行结果 所以我们需要对 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("%lsn", wstr.c_str()); system("pause"); return 0; } VS2022运行结果 MINGW-W64运行结果 由于 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] = '