簡體   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