繁体   English   中英

如何将非 ASCII 字符导入控制台?

[英]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。

tl;博士;

您需要先解码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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM