c++ - 为什么汉字经过编译器后会变成乱码?
问题描述
所以我正在编写一个程序,将一个中英文定义的.txt文件变成一个通过CLI运行的词汇训练器。但是,在 Windows 中,当我尝试在 VS2017 中编译它时,它变成了乱码,我不知道为什么。我认为它在 linux 中运行良好,但 windows 似乎把它搞砸了。这和windows中的编码表有关系吗?我错过了什么吗?我在 Linux 中编写了代码以及输入文件,但我尝试使用 windows IME 编写字符,结果仍然相同。我认为这张照片最能说明问题。谢谢
注意:根据要求添加了在 Windows 中显示的输入/输出示例。此外,输入是 UTF-8。
输入样本
人(rén),person
刀(dāo),knife
力(lì),power
又(yòu),right hand; again
口(kǒu),mouth
输出样本
人(rén),person
刀(dāo),knife
力(lì),power
又(yòu),right hand; again
口(kǒu),mouth
土(tǔ),earth
解决方案
TL;DR:Windows 终端讨厌 Unicode。你可以解决它,但它并不漂亮。
char
您在这里的问题与“对”无关wchar_t
。其实你的程序没有问题!只有当文本离开cout
并到达终端时才会出现问题。
您可能习惯于将 achar
视为“字符”;这是一个常见的(但可以理解的)误解。在 C/C++ 中,char
类型通常与8 位整数同义,因此更准确地描述为字节。
您的文本文件chineseVocab.txt被编码为 UTF-8。当你通过 读取这个文件时fstream
,你得到的是一串 UTF-8 编码的字节。
I/O 中没有“字符”之类的东西;您总是以特定的编码传输字节。在您的示例中,您正在从文件句柄 ( )中读取 UTF-8 编码的字节。fin
尝试运行它,您应该在两个平台(Windows 和 Linux)上看到相同的结果:
int main()
{
fstream fin("chineseVocab.txt");
string line;
while (getline(fin, line))
{
cout << "Number of bytes in the line: " << dec << line.length() << endl;
cout << " ";
for (char c : line)
{
// Here we need to trick the compiler into displaying this "char" as an integer:
unsigned int byte = (unsigned char)c;
cout << hex << byte << " ";
}
cout << endl;
cout << endl;
}
return 0;
}
这是我在我的(Windows)中看到的:
Number of bytes in the line: 16
e4 ba ba 28 72 c3 a9 6e 29 2c 70 65 72 73 6f 6e
Number of bytes in the line: 15
e5 88 80 28 64 c4 81 6f 29 2c 6b 6e 69 66 65
Number of bytes in the line: 14
e5 8a 9b 28 6c c3 ac 29 2c 70 6f 77 65 72
Number of bytes in the line: 27
e5 8f 88 28 79 c3 b2 75 29 2c 72 69 67 68 74 20 68 61 6e 64 3b 20 61 67 61 69 6e
Number of bytes in the line: 15
e5 8f a3 28 6b c7 92 75 29 2c 6d 6f 75 74 68
到现在为止还挺好。
问题从现在开始:您想将那些相同的 UTF-8 编码字节写入另一个文件句柄 ( cout
)。
cout
文件句柄连接到您的 CLI(“终端”、“控制台”、“外壳”,无论您想怎么称呼它)。CLI 从中读取字节cout
并将其解码为字符以便显示。
Linux 终端通常配置为使用UTF-8 解码器。好消息!您的字节是 UTF-8-encoded,因此您的 Linux 终端的解码器与文本文件的编码相匹配。这就是终端中一切看起来都不错的原因。
另一方面,Windows 终端通常配置为使用依赖于系统的解码器(您的似乎是DOS 代码页 437)。坏消息!您的字节是 UTF-8-encoded,因此您的 Windows 终端的解码器与文本文件的编码不匹配。这就是为什么终端中的一切看起来都是乱码的原因。
好的,那你怎么解决这个问题?不幸的是,我找不到任何可移植的方式来做到这一点......您需要将您的程序分成 Linux 版本和 Windows 版本。在 Windows 版本中:
- 将您的 UTF-8 字节转换为 UTF-16 代码单元。
- 将标准输出设置为 UTF-16 模式。
- 写到
wcout
而不是cout
- 告诉您的用户将他们的终端更改为支持中文字符的字体。
这是代码:
#include <fstream>
#include <iostream>
#include <string>
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
using namespace std;
// Based on this article:
// https://msdn.microsoft.com/magazine/mt763237?f=255&MSPPError=-2147217396
wstring utf16FromUtf8(const string & utf8)
{
std::wstring utf16;
// Empty input --> empty output
if (utf8.length() == 0)
return utf16;
// Reject the string if its bytes do not constitute valid UTF-8
constexpr DWORD kFlags = MB_ERR_INVALID_CHARS;
// Compute how many 16-bit code units are needed to store this string:
const int nCodeUnits = ::MultiByteToWideChar(
CP_UTF8, // Source string is in UTF-8
kFlags, // Conversion flags
utf8.data(), // Source UTF-8 string pointer
utf8.length(), // Length of the source UTF-8 string, in bytes
nullptr, // Unused - no conversion done in this step
0 // Request size of destination buffer, in wchar_ts
);
// Invalid UTF-8 detected? Return empty string:
if (!nCodeUnits)
return utf16;
// Allocate space for the UTF-16 code units:
utf16.resize(nCodeUnits);
// Convert from UTF-8 to UTF-16
int result = ::MultiByteToWideChar(
CP_UTF8, // Source string is in UTF-8
kFlags, // Conversion flags
utf8.data(), // Source UTF-8 string pointer
utf8.length(), // Length of source UTF-8 string, in bytes
&utf16[0], // Pointer to destination buffer
nCodeUnits // Size of destination buffer, in code units
);
return utf16;
}
int main()
{
// Based on this article:
// https://blogs.msmvps.com/gdicanio/2017/08/22/printing-utf-8-text-to-the-windows-console/
_setmode(_fileno(stdout), _O_U16TEXT);
fstream fin("chineseVocab.txt");
string line;
while (getline(fin, line))
wcout << utf16FromUtf8(line) << endl;
return 0;
}
在我的终端中,将字体更改为MS Gothic后,它看起来基本正常:
有些字符仍然混乱,但这是由于字体不支持它们。
推荐阅读
- javascript - 如何使用选择选项中的数组值作为 url 中的参数?
- javascript - 画布上的粒子被绘制,但没有动画?
- database - 使用映射表保持一致性的最佳方法
- python - BeautifulSoup 的编码/解码问题
- git - Github SSH 访问:权限被拒绝(公钥)
- javascript - 将 JSON 导入 chart.js 的数据和标签
- mips - 以下表达式在 MIPS 中是否正确
- leaflet - Ionic OpenStreetMap 与 Leaflet 的集成
- css - css关键帧动画图像循环
- android - 无法弄清楚如何将此字段保存到数据库中