[英]How to import non-ASCII characters into console?
我一直在为此挠头,我需要一些帮助。 基本上我想要代码做的是将一系列非 ASCII 符号读入一个空的预设数组,然后我打印它们以查看它们是否被读取,而它们目前没有被读取。 记事本可以很好地显示它们,但由于某种原因 C++ 不能将它们识别为有效字符,强烈推荐任何仅关于代码而不更改计算机内部设置的建议。
char displayCharacters[5] = {};
try {
instream.open("characters.txt");
instream >> displayCharacters;
cout << "Here is the first symbol: " << displayCharacters[4];
}
catch (exception) {
cout << "Something went wrong with the file handling.";
}
是的,我已经正确设置了内流,从 iostream 的导入和使用命名空间 std 中使用了 cout。 以下是文件包含的内容:
█
▀
▄
▓
编辑:如果您需要知道,该文件是 UTF-8。
您需要先解码UTF-8 才能对其进行索引。 请继续阅读以获取比我预期要写的更多的详细信息……
C++ stream 不支持编码 - 它只是一个 stream 字节。 例如,转储整个 UTF-8 字符串的代码可以正常工作:
#include <iostream>
#include <sstream>
#include <string>
int main() {
// Simulate your `instream` using an `std::stringstream`
std::stringstream instream;
// Load the simulated `instream` using a UTF-8 string literal [1]
instream << u8"█\n \n▀\n▄\n▓\n";
// Print entire `instream`
std::cout << instream.rdbuf();
}
[1]: https://en.cppreference.com/w/cpp/language/string_literal
您的问题来自 UTF-8 编码本身。 UTF-8 是多字节编码。 某些字符(尤其是 ASCII 字符)被编码为单个字节。 例如,字母a
被编码为值 97(十六进制的0x61
)。
让我们看一下您要打印的五个字符:
字符 | Unicode 代码点 | UTF-8编码 | Unicode 名称 |
---|---|---|---|
█ |
U+2588 |
0xe2 0x96 0x88 |
全块 |
|
U+20 |
0x20 |
空格(没有链接;这个只是普通的 ASCII ) |
▀ |
U+2580 |
0xe2 0x96 0x80 |
上半块 |
▄ |
U+2584 |
0xe2 0x96 0x84 |
下半块 |
▓ |
U+2593 |
0xe2 0x96 0x93 |
暗影 |
UTF-8 编码是这里有趣的部分——这就是每个字符如何作为字节序列存储在 UTF-8 编码文件中的方式。 对于四个块图字符(我们将忽略空格,因为它只是一个单字节字符),编码需要三个字节。
但是,如果代码点只有两个字节长,为什么编码需要三个字节呢?
好问题。 让我们分解第一个字符:
0xe2 0x96 0x88
11100010 10010110 10001000
AAAA^^^^ BB^^^^^^ BB^^^^^^
二进制文件下方的注释指示编码的工作方式。
由于字符的代码点太大而无法放入单个字节,因此 UTF-8 将其分成多个字节。 但是,必须有一种方法来确定字节序列表示单个字符,而不仅仅是简单字符序列。 这就是字节前缀(A、B 和 C)发挥作用的地方。 多字节序列中的第一个字节以1
位序列开始,表示编码字符中的总字节数,后跟一个终止0
。 这里我们需要三个字节,所以我们有1110
(A)。
其余两个字节的前缀表明它们是连续字节(即不应将它们视为字符的开头)。 连续字节的前缀定义为10
(B)。
删除这些前缀后,剩余的位(用脱字符 [ ^
] 标记)被打包并解析以检索编码的代码点。
单字节字符(即从 0 到 127 字符的基本 US-ASCII 平面)只需要 7 位进行编码,因此前缀0
表示该字符没有连续字节。
我之前说过“您的问题来自 UTF-8 编码本身”。 好吧,我撒谎了。 对不起。 您的问题来自尝试将 UTF-8 编码数据作为纯字节序列读取。
使用上面的编码表,让我们看一下文件中的原始字节(假设单个\n
终止每一行):
e2 96 88 0a 20 0a e2 96 80 0a e2 96 84 0a e2 96 93 0a
\--01--/ 02 \--03--/ \--04--/ \--05--/
我已经用它们的行号标记了这些字符。
从这个转储中,您可以轻松地看到问题代码的 output 将是:
char displayCharacters[5] = {};
std::cout << "Here is the first symbol: " << displayCharacters[4];
这是一个空格,记住,stream 不知道文件的编码,所以它只是吐出一个字节序列(C/C++ 中的char
只是一个 8 位变量)。 您的数组 ( displayCharacters
) 包含上面显示的字节序列,因此下标它以获取第四个(零索引)元素返回字节0x20
。
你在这里真的很幸运。 将 UTF-8 数据索引为原始字节通常会导致更丑陋的错误。 还记得那些连续字节(开始10
)吗? 如果您提取并尝试自己打印其中一个,您的终端将不知道如何处理它。 与多字节序列的开头类似(前缀11
)。
正确索引 UTF-8 字符串很难。 你几乎肯定会想要一个库来处理它。
根据相关文件的用途和/或来源,您可能需要考虑使用固定宽度编码,例如UTF-32 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.