文章摘要
前言 阅读前,已默认读者已经熟知 编码 , 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("%s\n", 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("%ls\n", 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] = '\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; } 参考 C++中字符编码的转换(Unicode、UTF-8、ANSI) FlushHip-CSDN博客 c++ utf8前言
阅读前,已默认读者已经熟知编码,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字符串,可能得到以下结果
涓�鍥絅O.1


所以我们需要对UTF8编码的string进行转换,转换成UTF16的wstring后再输出就可以正常显示了了


由于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
参考
本文采用 CC BY-NC-SA 4.0 协议进行授权,如无注明均为原创,转载请注明出处。
标题:C++ Windows编程中的一些编码处理作者:九仞之行链接:http://dev.styunlen.cn/archives/post-1406.html
