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