最近在Windows系统下的VS2019写代码,遇到一些编码方面的问题,给大家排排雷。顺便提一下,Windows内核中使用UTF-16 LE编码来表达所有符号,已经足够了,用户态不同地区使用的默认编码不一样,咱们这儿使用的是GBK。
GB2312 全称为《信息交换用汉字编码字符集・基本集》,是中国 1980 年发布的第一个官方中文编码标准,主要解决早期计算机处理中文的问题。收录了6763个简体汉字和682个非汉字字符(数字、标点符号和其他拉丁字母)。GBK是对GB2312编码的扩展,完全兼容GB2312,汉字提升到了21003个,图形符号883个。这两种编码都使用两个字节表达一个符号,属于定长编码。我当前系统的Code Page如下:

安全狗的GBK编码是
Unicode是一个字符集,它为每个字符分配了唯一的码点,不是一种编码方式。它可以使用的编码方式有如下三种:

安全狗的UTF-8编码是
从这三个编码中可以看出,UTF-8是相对来说比较省空间的,但随机访问效率最低,UTF-32不省空间,但随机访问效率最高。原理其实和内存对齐,不论是分配内存时的页对其还是结构体的8字节或四字节对其一样。
我当前的Code Page时936,GBK编码,如何在控制台输出一串中文内容呢?有几种方法?我使用的C++代码如下:
文件默认以GBK编码保存,把上面代码进行编译,VS也是以GBK对文件进行编译的。

编译之后文件中的字符串如下:

运行编译之后的文件,结果如下:

上面output_w指向的内容被以UTF-16 LE编码编译到了exe文件中,输出的时候是以GBK编码展示,自然就没有打印出预期的结果。然后,将上面代码以UTF-8保存到文件中,然后VS项目属性->C/C++->命令行->额外选项中增加/utf-8,命令行执行结果如下:

全是乱码,因为输出的内容并不是以GBK编码的。执行chcp 65001切换控制台Code Page为UTF-8,输出的内容如下:

发现宽字节的内容没有正常输出,因为L"安全狗"这部分内容在exe中是UTF-16 LE编码,不管.cpp文件是gbk编码还是utf-8编码。那么如何正常输出中文内容呢? 让输出的内容都是和控制台Code Page一致,比如:都是GBK编码。修改上面的代码为
结果如下:

python2的py文件默认使用ascii,如果出现ascii码之外的内容,需要在开头声明文件的编码格式,比如:# coding:utf-8。而且python2的解释器根据声明的编码或默认的编码来判断代码中的内容,如果出现不支持的字符,就会报错,如下:

python2控制台支持输出的编码除了当前Code Page,还有Unicode。

python3的py文件必须是以utf-8编码进行保存,其他编码格式如果出现utf-8不支持的编码将会报错。

不论控制台Code Page是936(GBK)还是65001(UTF-8),都可以正常输出,如下:

如何启动一个带有路径中带有中文的可执行程序呢?下面使用三种方法来启动,以中文路径"D:\test\测试\安全狗.exe"目标可执行程序。
传递给check_output函数的路径必须是gbk编码的.
python3会自动将编码处理好,你只需要将关注py文件的编码格式就好,必须是utf-8。
上面的.cpp以gbk编码保存,正常编译就会得到下面的结果:

windows xxxA的API最终会调用xxxW的API,其中一个环节就是进行了编码转换,转换为UTF-16 LE的。如果使用CreateProcessA,需要保证输入的路径是当前的代码页。举个例:CHAR commandLine[] = "D:\test\测试\安全狗.exe"; 如果以UTF-8编码传入到CreateProcessA中,进程将会创建失败,原因是找不到这个文件。
本篇文章介绍了C++和Python可能遇到的一些字符串打印的问题及背后的原因。核心还是在于Code Page和输出的字符串编码要匹配,否则输出的内容就是乱码。如果你想创建一个带有中文路径的程序,Python3的话会自动进行处理,Python2和Windows API传递的路径必须符合当前代码页。
安 -> \xb0\xb2
全 -> \xc8\xab
狗 -> \xb9\xb7
安 -> \xb0\xb2
全 -> \xc8\xab
狗 -> \xb9\xb7
安 -> \xe5\xae\x89
全 -> \xe5\x85\xa8
狗 -> \xe7\x8b\x97
安 -> \xe5\xae\x89
全 -> \xe5\x85\xa8
狗 -> \xe7\x8b\x97
安 -> \x89\x5b(LE) \x5b\x89(BE)
全 -> \x68\x51(LE) \x51\x68(BE)
狗 -> \xd7\x72(LE) \x72\xd7(BE)
安 -> \x89\x5b(LE) \x5b\x89(BE)
全 -> \x68\x51(LE) \x51\x68(BE)
狗 -> \xd7\x72(LE) \x72\xd7(BE)
安 -> x89\x5b\x00\x00(LE) \x00\x00\x5b\x89(BE)
全 -> \x68\x51\x00\x00(LE) \x00\x00\x51\x68(BE)
狗 -> \xd7\x72\x00\x00(LE) \x00\x00\x72\xd7(BE)
安 -> x89\x5b\x00\x00(LE) \x00\x00\x5b\x89(BE)
全 -> \x68\x51\x00\x00(LE) \x00\x00\x51\x68(BE)
狗 -> \xd7\x72\x00\x00(LE) \x00\x00\x72\xd7(BE)
#include <iostream>
int main(int argc, char* argv[])
{
const char* output = "安全狗";
printf("printf => %s\n", output);
std::cout << "std::cout => " << output << std::endl;
const wchar_t* output_w = L"安全狗";
wprintf(L"wprintf => %ws\n", output_w);
std::wcout << "std::wcout => " << output_w << std::endl;
return 0;
}
#include <iostream>
int main(int argc, char* argv[])
{
const char* output = "安全狗";
printf("printf => %s\n", output);
std::cout << "std::cout => " << output << std::endl;
const wchar_t* output_w = L"安全狗";
wprintf(L"wprintf => %ws\n", output_w);
std::wcout << "std::wcout => " << output_w << std::endl;
return 0;
}
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
const char* output = "安全狗";
printf("printf => %s\n", output);
std::cout << "std::cout => " << output << std::endl;
const wchar_t* output_w = L"安全狗";
int size = WideCharToMultiByte(936, 0, output_w, -1, NULL, 0, NULL, NULL);
char* new_output_w = new char[size];
WideCharToMultiByte(936, 0, output_w, -1, new_output_w, size, NULL, NULL);
printf("new_output_w => %s\n", new_output_w);
std::wcout << L"std::wcout => " << new_output_w << std::endl;
return 0;
}
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
const char* output = "安全狗";
printf("printf => %s\n", output);
std::cout << "std::cout => " << output << std::endl;
const wchar_t* output_w = L"安全狗";
int size = WideCharToMultiByte(936, 0, output_w, -1, NULL, 0, NULL, NULL);
char* new_output_w = new char[size];
WideCharToMultiByte(936, 0, output_w, -1, new_output_w, size, NULL, NULL);
printf("new_output_w => %s\n", new_output_w);
std::wcout << L"std::wcout => " << new_output_w << std::endl;
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!