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

标签 c++ c++17

我已经为此挠了一下头,需要一些帮助。基本上,我希望代码执行的操作是将一系列非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导入中使用了cout并使用了命名空间std。该文件包含以下内容:
█
 
▀
▄
▓
编辑:如果您需要知道该文件是UTF-8。

最佳答案

tl; dr;
您需要先对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();
}
[1]:https://en.cppreference.com/w/cpp/language/string_literal
您的问题来自于UTF-8编码本身。 UTF-8是多字节编码。一些字符(特别是ASCII字符)被编码为单个字节。例如,字母a被编码为值97(十六进制的0x61)。
让我们看一下您要打印的五个字符:


烧焦
Unicode代码点
UTF-8编码
Unicode名称

U+25880xe2 0x96 0x88 FULL BLOCK
U+200x20SPACE(无链接;这只是纯ASCII)
U+25800xe2 0x96 0x80 UPPER HALF BLOCK
U+25840xe2 0x96 0x84 LOWER HALF BLOCK
U+25930xe2 0x96 0x93 DARK SHADE


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--/
我已经用字符的行号标记了字符。
从这个转储中,您可以轻松地看到问题代码的输出是:
char displayCharacters[5] = {};
std::cout << "Here is the first symbol: " << displayCharacters[4];
这是一个空间!请记住,流不知道文件的编码,因此它只是吐出一个字节序列(C/C++中的char只是一个8位变量)。您的数组(displayCharacters)包含上面显示的字节序列,因此对它进行下标以获得第四个(零索引)元素将返回字节0x20
你在这里真的很幸运。将UTF-8数据索引为原始字节通常会导致难看得多的错误。还记得那些连续字节(开始于10)吗?如果您提取并尝试自行打印其中之一,则终端将不知道如何处理它。与多字节序列的开头类似(前缀11)。
正确索引UTF-8字符串是hard。几乎可以肯定,您需要一个图书馆来处理它。
根据相关文件的用途和/或来源,您可能需要考虑使用固定宽度的编码,例如UTF-32

关于c++ - 如何将非ASCII字符导入控制台?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65410642/

相关文章:

c++ - 为什么 const auto &p{nullptr} 工作,而 auto *p{nullptr} 在 C++17 中不起作用?

c++ - 如何将 std::string_view 转换为 double ?

c++ - 我应该使用什么样的数据结构来实现 UPGMA?

c++ - 如何简化 std::variant 类类型

c++ - 避免对 iOS 应用程序进行逆向工程,这可能吗?

javascript - Qt 和 JavaScript 映射数据

c++ - 两阶段查找 : can I avoid "code bloat"?

c++ - 无法包含 winhttp.h (带有代码::blocks/mingw)c++

c++ - OpenGL 中的 Oren-Nayar 光照(如何在片段着色器中计算 View 方向)

c++ - 如何在 Vista/Server 2008 上使用 std::shared_mutex?