UTF-8 的理解
举个简单的例子:
Unicode 只是一个业界标准,具体一个字符占多少字节,取决于编码方式,包括 UTF-8 UTF-16 GB2312 等
“汉” 在 UTF-8 中占到 3 个字节,即 E6 B1 89
在 GB2312 中占到 2 个字节,即 BA BA,一般我们的电脑都是以 GB2312 编码中文的
注意:如果以 VS 编译器以 Unicode 编码,则是 0x6C49,同样占 2 个字节,多字节则是 BA BA
wchar_t aaa[] = L"汉"; // 0x6c49 数组占 4 个字节,包括占两个字节的 '\0' char bbb[] = "汉"; // 0xBABA 数组占 3 个字节,包括占一个字节的 '\0'
在 UTF-16 中占到 2 个字节,即 0x6C49
UTF-16 LE是windows上默认的Unicode编码方式,使用wchar_t表示。所有wchar_t *类型的字符串(包括硬编码在.h/.cpp里的字符串字面值),VC都自动采用UTF-16的编码(字符串字面值,literal string,存在很多坑。特别是char *类型的字面值,最终内存使用何种编码方式完全取决于当前文件的编码方式。也就是说当前文件如果是GBK编码的,那么文件里char * str = "中午",str指向的内存字符串二进制是使用GBK编码的。如果文件编码是UTF-8,那么内存是使用UTF-8编码。所以为什么一直要强调字符串应该放在资源文件里,而不是硬编码在.h/.cpp文件里!)。
使用 UTF-8 的优势,没有字节序的概念,特别适合用于字符串的网络数据传输,不用考虑大小端问题。对于非英文网页(对于我们而言,简单说东亚文字网页),能够避免各种乱码问题。
相关链接:
补充:
ANSI 编码是各个国家不同语种下的字符编码,其字符的编码值只在该语种中有效,不是全球统一编码的,比如中文的 GB2312 编码就是简体中文的 ANSI 编码。
Unicode 编码则是全球统一的双字节编码,所有语种的字符在一起统一的编码,每个字符的编码都是全球唯一的。
UTF8 编码是一种可变长的宽字节编码,也是一种全球统一的字符编码。
对于大小写英文字母和数字的 ANSI 编码,是字符 ASCII 码,英文字母和数字的 ACSII 码是全球统一的,比如大写字母 A 的 ASCII 码是 65(十六进制是 41H),数字 0 的A SCII 码是 48(十六进制是 30H)。所以在所有语种中,大小写字母及数字的 ANSI 编码,都是能识别的。不同语种下的本地文字字符,一般是不能相互识别的。
使用中文 ANSI 编码的字符串开发的程序(代码中使用的都是中文字符串,使用的是 ANSI 窄字节编码),拿到俄文操作系统中可能显示的都是乱码,因为在俄文的 ANSI 编码中只识别俄文的 ANSI 编码出来的字符串,无法识别中文 ANSI 编码的字符串。这里主要有两类字符乱码问题,一是 UI 界面上显示的文字是乱码;二是使用路径去创建文件或访问文件时会因为路径中的字符是乱码,导致文件创建或访问失败。
Windows 系统主要使用 Unicode 编码,Linux 则使用 UTF8 编码,后台服务器一般使用的都是 Linux 系统,而客户端是运行在 Windows 操作系统上的。一般客户端与服务器交互的数据的字符串编码统一使用全球统一编码的 UTF8 编码。
客户端收到 UTF8 编码的字符串后,需要将 UTF8 字符换转换后显示在界面上。如果客户端使用的是 Unicode 编码字符集,将 UTF8 编码的字符串转换成 Unicode 编码的字符串后再显示到界面上;如果客户端使用的是多字节 ANSI 编码,则需要再将 Unicode 编码的字符串转成 ANSI 编码的字符串。
这里注意一下,UTF8 编码的字符串要转成 ANSI 编码的,不能直接将 UTF8 转成 ANSI,需要先将 UTF8 转成 Unicode,然后再将 Unicode 转成ANSI。
现在的 Windows 程序基本都用 Unicode 字符编码了,工程属性中将字符集都设置成了 Unicode 字符集,代码中都使用 Unicode 编码的字符串。但是还有一些老的程序使用的还是 ANSI 窄字节的字符。那这些老的程序如何才能在外文的操作系统中正常运行呢?微软提供了一种兼容这些老程序的办法。
可以到 Windows 控制面板的区域语言设置中将非 Unicode 语言设置成程序中使用的字符语种即可
问题:为什么中文 ANSI 编码的程序在外文操作系统中会出现乱码?
解释:
程序中调用 API 函数 SetWindowTextA 给程序中的窗口设置文字或标题时,传入的字符串是 ANSI 窄字节编码的,而 SetWindowTextA 函数内部及底层的流程中会使用本地设置的 ANSI 字符集编码库将 ANSI 编码的字符串转成Unicode 编码的字符串后再设置到窗口中,最终界面上看到的文字是 Unicode 编码的文字。所以在将中文字符转换成 Unicode 时,如果使用的是本地设置的英文字符集编码进行转换,则会出现乱码;如果使用中文简体的字符集编码进行转换,则能正常显示。
所以,要让使用中文 ANSI 编码字符的程序能在英文操作系统中正常显示并运行,需要将英文操作系统中区域语言设置项中的“非 Unicode 程序的语言”设置成中文才行。
窄字节与宽字节的转换是调用 MultiByteToWideChar 和 WideCharToMultiByte 接口中的 CP_ACP 标记对应的本地 ANSI 字符集编码库
比如:如果将非 Unicode 语言设置成英语(美国),则使用英文的 ANSI 字符编码库;如果设置成中文简体,则使用中文简体的 ANSI 字符集编码库。
参考:一文带你弄懂C++中的ANSI、Unicode和UTF8三种字符编码
回顾:
“猫” 的 UTF8 编码是三个字节,分别是 E7 8C AB,对应的二进制是 11100111 10001100 10101011
根据上图提示的三字节模板,提取出 Unicode 字节 =》11100111 10001100 10101011
提出的二进制组合在一起就是 0111001100101011,转为十六进制就是 0x732B
查看 Unicode 编码表,可以发现对应的是“猫”
编码表链接:https://sf-zhou.github.io/programming/chinese_unicode_encoding_table.htm
反过来也可以用 Unicode 转为 UTF8,规则是一样的