本文发布于 2022-01-16, 最后更新于 1684 天前,其中的信息可能已经有所发展或是发生改变。

文章摘要

前言 阅读前,已默认读者已经熟知 编码 , ASCII , 字符集 等与 编码 相关的概念,前言中也会对这些概念进行粗略的讲解,若看不懂,可以先去学习相关概念,再来阅读本篇文章。 由于web的普及,各国家之间的信息交换成为可能,然而不同国家对其文字定义的 字符集 并不相同,这也间接促成了 Unicode 的诞生。 什么是 字符集 呢?可以理解成 字符 与 数字 (数码)之间的 映射集 ,为了方便,…

前言

由于web的普及,各国家之间的信息交换成为可能,然而不同国家对其文字定义的字符集并不相同,这也间接促成了Unicode的诞生。

什么是字符集呢?可以理解成字符数字(数码)之间的映射集,为了方便,接下来的数字(数码)我都使用16进制进行表示。

比如"中"这个字在GBK字符集中就对应着d6d0这个数字,而在UNICODE字符集中则对应着4e2d这个数字。

但是,单单有字符集并没有完全解决与信息交换以及存储相关的问题,例如,如何将字符集中的字符进行编码并用于计算机间的交换储存,就是个问题。

举个实际例子吧,已知“中国NO.1”的UNICODE码4e2d 56fd 004e 004f 002e 0031,若简单的直接依照这个十六进制字符串对其进行保存,那么字符串中英文字符的占用空间将会是ASCII码的两倍!这也太浪费了。

然后,UTF8,一个UNICODE字符集的可变长编码方案诞生了。这里就不展开讨论UTF8的编码原理了,而是直接阐述windows编程中的相关处理办法。

解决方案

UTF8 -> UTF16

由于UTF8的可变长特性,在计算机中我们一般使用bytechar序列来储存U8字符串,在C++中,char序列可以用string代替。可能有疑惑会什么不能用wstring,因为wstring的基本单位是两字节长度的wchar_t,而对于使用UTF8编码后编码长度为三字节的汉字来说,这就可能导致存储相关的问题(字节无法对齐了)。

但这里有个问题啊,windows操作系统默认使用UNICODEUTF16编码,若在控制台直接输出UTF8字符串,可能得到以下结果

x86asm (Auto identified)
#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进行转换,转换成UTF16wstring后再输出就可以正常显示了了

cpp (Auto identified)
#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运行结果​

UNICODE <-> ANSI

其实在Windows中,string默认的编码是ANSI,中文电脑上ANSI其实是GBK

那么UTF16编码的wstring如何和ANSI(GBK)编码的string进行转换呢?WindowsAPI提供了两个函数WideCharToMultiByteMultiByteToWideChar。函数名称中的WideChar代表UTF16编码的字符串,而MultiByte代表ANSI编码的字符串

具体实现可以参照以下代码

cpp (Auto identified)
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] = ' '; ret = m_wchar; delete[] m_wchar; return ret; }
cpp (Auto identified)
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] = ' '; ret = m_char; delete[] m_char; return ret; }

参考

九仞之行

严于律己,宽以待人,深自警省,讷言敏行

订阅
💬 评论 (7)
S
Styunlen博主Chrome 89 · Android日本

写这篇博文的起因是一位群友提了一个wstring为什么无法正确输出的问题

J
jtryChrome 86 · Android山东

厉害👍🏻

第儿Edge 99 · Windows 10中国

好厉害鸭

第儿Edge 99 · Windows 10中国

写这篇博文的起因是一位群友提了一个wstring为什么无法正确输出的问题

@Styunlen

你在手机上用chrome?

S
Styunlen博主Chrome 66 · Android印度

你在手机上用chrome?

@第儿

其实是手机版的Edge啦

豆焰Chrome 103 · Windows 10中国

真不错

S
Styunlen博主Chrome 66 · Android印度

真不错

@豆焰

嘿嘿୧(๑•̀⌄•́๑)૭

编辑器加载中…