如何在C++中处理字符串中的非ASCII字符?

3

在编写程序时,我遇到了特殊字符和常规字符的组合问题。当我分别将它们打印到控制台时,它们正常工作,但是当我在同一行中打印特殊字符和普通字符时,会出现错误的字符而不是预期的输出。

我的代码:
#include <fstream>
#include <iostream>
#include <string>

using namespace std;

void initCharacterMap(){
    const string normal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_[]{};':\",.<>/?";
    const string inverse = "∀Ↄ◖ƎℲ⅁HIſ⋊⅂WᴎOԀΌᴚS⊥∩ᴧMX⅄Zɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz12Ɛᔭ59Ɫ860¡@#$%^⅋*)(-‾][}{؛,:„'˙></¿";

    cout << normal << endl;

    for(int i=0;i<normal.length();i++){
        cout << normal[i];
    }
    cout << endl;

    cout << inverse << endl;

    for(int i=0;i<inverse.length();i++){
        cout << inverse[i];
    }
    cout << endl;

    for(int i=0;i<inverse.length();i++){
        cout << normal[i] << inverse[i] << endl;
    }
}

int main() {
    initCharacterMap();
    return 0;
}

并且控制台输出: https://paste.ubuntu.com/p/H9bqh67WPZ/

当在控制台中查看时,\XX字符会显示为未知字符符号,当我打开该日志时,警告说某些字符无法查看,并且编辑可能会损坏文件。

如果有人能给我一些建议来解决这个问题,将不胜感激。

编辑: 在遵循Marek R答案中的建议后,情况大有改善,但这仍然没有给我想要的结果。 新代码:

#include <fstream>
#include <iostream>
#include <string>

using namespace std;

void initCharacterMap(){
    const wchar_t normal[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_[]{};':\",.<>/?";
    const wchar_t inverse[] = L"∀Ↄ◖ƎℲ⅁HIſ⋊⅂WᴎOԀΌᴚS⊥∩ᴧMX⅄Zɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz12Ɛᔭ59Ɫ860¡@#$%^⅋*)(-‾][}{؛,:„'˙></¿";

    wcout << normal << endl;

    for(int i=0;i<sizeof(normal)/sizeof(normal[0]);i++){
        wcout << normal[i];
    }
    wcout << endl;

    wcout << inverse << endl;

    for(int i=0;i<sizeof(inverse)/sizeof(inverse[0]);i++){
        wcout << inverse[i];
    }
    wcout << endl;

    for(int i=0;i<sizeof(inverse)/sizeof(inverse[0]);i++){
        wcout << normal[i] << inverse[i] << endl;
    }
}

int main() {
    initCharacterMap();
    return 0;
}

新的控制台输出: https://paste.ubuntu.com/p/hcM7JB99zj/

现在,我不再遇到将字符串内容输出拼接在一起的问题了,但现在的问题是所有非ASCII字符都被替换成了问号。有没有办法让这些字符正确地输出?


1
看一下 std::stringstd::wstring。后者特别用于表示ASCII范围之外的字符(wchar_tchar大)。 - Fureeish
1
首先,您需要停止称它们为“特殊字符”,并找出您实际存储的内容 ;) - Lightness Races in Orbit
1
但是所有字符都是特殊的! - user4581301
谢谢大家的建议,我已经按照它们进行了更新,并做出了相应的修改。 - The_Fireplace
1个回答

2
很可能你的代码正在使用UTF-8编码,这意味着单个字符可以占据1到4个字节。 请注意,inverse.size()的值比你预期的要大。 std::string不知道任何关于编码的信息,所以它将每个字节视为一个字符。输出控制台按照相应的编码解释字节序列并显示正确的字符。
当你分别打印每个字符串中的每个字节时,它能够工作,因为顺序是正确的。 当你从一个字符串中打印一个字节,并从另一个字符串中打印一个字节时,会出现混乱。
最简单的解决方法是使用std::wstring wchar_tL"some literal"。它应该在你的情况下工作,但正如下面的注释指出,在一些平台上某些字符可能不适合单个宽字符。 如果你想了解更多,请阅读有关不同字符编码的内容。
解决问题的另一种方法是使用一个映射,将字节序列(字符串)转换为其他序列(字符串)。 C++11:
auto dictionary = std::unordered_map<std::string, std::string> {
    { "A", "∀" },
    { "B", "" },
    { "C", "Ↄ" },
    { "D", "◖" },
    … … …
}


编辑 我已经测试了你的新代码,你需要添加配置输出流语言环境的代码。

在我的Mac上(使用波兰语环境),使用clang编译时,应用程序会忽略inverted值(wcout进入无效状态),但是当设置语言环境时,一切都像你期望的那样工作。

#include <fstream>
#include <iostream>
#include <string>
#include <locale>

using namespace std;

void initCharacterMap(){
    wcout.imbue(locale(""));

    const auto normal = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_[]{};':\",.<>/?"s;
    const auto inverse = L"∀Ↄ◖ƎℲ⅁HIſ⋊⅂WᴎOԀΌᴚS⊥∩ᴧMX⅄Zɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz12Ɛᔭ59Ɫ860¡@#$%^⅋*)(-‾][}{؛,:„'˙></¿"s;

    wcout << normal << endl;

    for(auto ch : normal){
        wcout << ch;
    }
    wcout << endl;

    wcout << inverse << endl;

    for(auto ch : inverse){
        wcout << ch;
    }
    wcout << endl;

    for(size_t i=0; i < inverse.length(); ++i){
        wcout << normal[i] << inverse[i] << endl;
    }
}

int main() {
    initCharacterMap();
    return 0;
}

我怀疑你使用的编译器标准库也不知道如何使用默认语言环境进行转换,因此会打印出问号而非实际字符。加入这两行代码(包括includeimbue),应该可以解决问题。如果仍未解决,请提供有关您的平台和编译器的信息。

https://wandbox.org/permlink/nTYi5RbZgZXclE5r


3
这个答案一直很好,直到最后一行,那是错误的。 - Lightness Races in Orbit
1
我实际上将我的踩转为了赞,因为大部分是正确和有用的。但是,请修复最后一行。 - Lightness Races in Orbit
1
固定并提供替代方案 - Marek R
这个答案对我的问题帮助很大,但是现在我又遇到了一个新的问题。在输出中,所有的非ASCII字符都被替换成了问号。你有任何想法如何解决这个问题吗? - The_Fireplace
1
是的,就像我之前提到的那样,从不支持编码的单字节“字符”切换到不支持编码的双字节“字符”并不能解决问题,它只是改变了问题的形式。假设使用UTF-8,你实际上需要的是一个能够识别和处理UTF-8的库。幸运的是,这并不难找到。我不记得这个特性集是否包含你需要的内容,但是http://utfcpp.sourceforge.net/非常适用于快速轻量级的工作。(不幸的是,SourceForge的一半已经下线了整整一天-.-) - Lightness Races in Orbit
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接