我一直在挠头一段时间,我需要一些帮助。基本上我想要代码做的是将一系列非 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.";
}
Run Code Online (Sandbox Code Playgroud)
是的,我已经正确设置了输入流,从 iostream 的导入和使用命名空间 std 中使用了 cout。以下是该文件的内容:
?
?
?
?
Run Code Online (Sandbox Code Playgroud)
编辑:如果您需要知道,该文件是 UTF-8。
您需要先解码UTF-8,然后才能对其进行索引。继续阅读以获取比我预期要写的更多的细节……
C++ 流不是编码感知的——它只是一个字节流。例如,这个转储整个 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();
}
Run Code Online (Sandbox Code Playgroud)
[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^^^^^^
Run Code Online (Sandbox Code Playgroud)
二进制文件下方的注释指示编码的工作方式。
由于字符的代码点太大而无法放入单个字节,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--/
Run Code Online (Sandbox Code Playgroud)
我已经用它们的行号标记了这些字符。
从这个转储中,你可以很容易地看到有问题的代码的输出是什么:
char displayCharacters[5] = {};
std::cout << "Here is the first symbol: " << displayCharacters[4];
Run Code Online (Sandbox Code Playgroud)
这是一个空间!请记住,流不知道文件的编码,因此它只是吐出一个字节序列(charC/C++ 中的 a 只是一个 8 位变量)。您的数组 ( displayCharacters) 包含上面显示的字节序列,因此为其添加下标以获取第四个(零索引)元素返回 byte 0x20。
你在这里真的很幸运。将 UTF-8 数据索引为原始字节通常会导致更难看的错误。还记得那些连续字节(开始10)吗?如果您提取并尝试自行打印其中之一,您的终端将不知道如何处理它。与多字节序列(前缀11)的开头类似。
正确索引 UTF-8 字符串很困难。您几乎肯定需要一个库来处理它。
根据相关文件的用途和/或来源,您可能需要考虑固定宽度的编码,例如UTF-32。
| 归档时间: |
|
| 查看次数: |
93 次 |
| 最近记录: |