1.背景
linux系统和mac os默认使用utf8编码,windows系统上通常是跟随系统设置,如果系统选择为中文地区的话,默认为GBK编码。
windows 10 1703开始,支持把windows编码设置为utf8 。
IDE上一般也是默认是这个配置,但是IDE可以选择编码源码保存为什么格式。
2.跨平台项目遇到的问题
由于linux和windows共用一个代码库,所以源码都使用同一编码格式,在我的情况,我把源码保存为utf8,并在windows上把ide配置为 utf8 不带BOM, FL 格式
![image-20230917190001252](image-20230917190001252.png)
BOM,windows字符前加上额外字节的信息,用来让windows认识字符串属于哪种编码
FL , 换行符windows上是CRFL, Mac os是CR ,Linux是FL
同时windows上把编译器设置为输出utf8 格式,
qmake使用 CONFIG += utf8_source
配置,它会根据编码器自动加上utf8的编码选项
至此为止,世界如此美好, 一切显示正常,甚至还能用emoji 表情字符
直到打开文件发现打不开, 调用系统命令行无故执行错误 ,控制台显示乱码
3.解决方法
3.1 使用windows api转换utf8为gbk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include <sstream>
#include <locale>
#include <codecvt>
inline std::string utf8_to_gbk(const std::string& str)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
std::wstring tmp_wstr = conv.from_bytes(str);
//GBK locale name in windows
const char* GBK_LOCALE_NAME = ".936";
std::wstring_convert<std::codecvt_byname<wchar_t, char, mbstate_t>> convert(new std::codecvt_byname<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));
return convert.to_bytes(tmp_wstr);
}
inline std::string gbk_to_utf8(const std::string& str)
{
//GBK locale name in windows
const char* GBK_LOCALE_NAME = ".936";
std::wstring_convert<std::codecvt_byname<wchar_t, char, mbstate_t>> convert(new std::codecvt_byname<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));
std::wstring tmp_wstr = convert.from_bytes(str);
std::wstring_convert<std::codecvt_utf8<wchar_t>> cv2;
return cv2.to_bytes(tmp_wstr);
}
|
每当需要和系统交互时,先把utf8转换为gbk ,例如 打开文件,调用系统命令行
如果调用系统管道,返回了字符串,分别调用子进程,重定向了字符回显,也需要返回了字符串是gbk,要在程序里转换为utf8
这种方式比较麻烦,但也比较通用,只要是字符串就能转
3.2 使用 c++17 filesystem 解决文件路径
filesystem在gcc 5.4仍然属于实验阶段的功能,所以在gcc 5.4中,它的名称空间和头文件位置多了一级experimental
1
2
3
4
5
6
7
|
//gcc 5.4
#include <experimental/filesystem>
int main()
{
std::experimental::filesystem::path .....
}
|
如果是gcc 7 则需要去掉experimental这级 名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <filesystem>
void testFilePath(std::string filename)
{
std::ifstream file(filename);
if(file.good())
std::cout << filename << " 存在"<< std::endl;
else
std::cout << filename << " 不存在"<< std::endl;
}
int main()
{
//全是ascii
testFilePath("d:/privatekey.pem");
testFilePath("d:/数据判定.csv");
std::filesystem::path path = std::filesystem::u8path("d:/数据判定.csv");
std::ifstream file(path);
if(file.good())
std::cout << path << " 存在"<< std::endl;
else
std::cout << path << " 不存在"<< std::endl;
}
|
上面的代码如果使用utf8编译选项,且源码保存为utf8,且在代码页设置为utf8的控制台上运行,你将看到输出
1
2
3
|
d:/privatekey.pem 存在
d:/数据判定.csv 不存在
"d:/�����ж�.csv" 存在
|
这种方式可以十分方便的兼容多个平台,但std::filesystem::path只有c++17才有,并且,不能直接和std::string转换,
但是上面的示例代码使用std::cout 输出是正常的,只是被加上了引号, 说明可以使用流的方式写入std::string里,只是可能会被加上引号。
3.3 使用windows api解决 gbk系统环境控制台显示程序输出乱码
这种情况,可以使用3.1中的那种文件转换utf8编码,但是如果不小心把gbk转为gbk, 程序就会崩溃, 打印子进程回显时踩了一次坑
可以使用windows设置程序输出到控制台的代码页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#ifdef _MSC_VER
#define _AMD64_
#include <ConsoleApi2.h>
#include <winnls.h>
#endif
int main(int argc, const char** argv)
{
#ifdef _MSC_VER
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
}
|
>> Home
Comments