如何在C ++中使用字符串中的非ascii字符?

时间:2018-02-16 23:22:06

标签: c++ non-ascii-characters

在编写程序时,我遇到了使用特殊字符和常规字符组合的问题。当我将任一类型分别打印到控制台时,它们工作正常,但是当我在同一行中打印一个特殊和正常的字符时,会导致错误的字符而不是预期的输出。 我的代码:

#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 个答案:

答案 0 :(得分:0)

您的代码很可能是使用UTF-8编码。这意味着单个字符可以占用1到4个字节。 请注意,inverse.size()的值大于您的预期值。

std::string对编码一无所知,因此将每个字节视为一个字符。输出控制台正在解释各个编码中的byres序列,并显示正确的字符。

当你逐字节地打印每个字符串时,它会起作用,因为序列是正确的。 当你从一个字符串打印一个字节而从其他字符打印一个字节时会变得混乱。

最简单的解决方法是使用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进入无效状态),但是当设置语言环境时,一切都会像您期望的那样工作。 / p>

#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;
}

https://wandbox.org/permlink/nTYi5RbZgZXclE5r

我怀疑编译器中的标准库也不知道如何使用默认语言环境执行转换,因此它会打印问号而不是实际的章程。所以添加这两行(includeimbue),它应该工作。如果没有,则提供有关您的平台和编译器的信息。