C++编码理解以及中文显示

要正确处理编码,需要理解一个程序从代码到用户阶段的几个编码:

  • 代码编码:代码文件使用的编码
  • 执行编码:编译完的可执行文件中使用的编码
  • 显示编码:在用户的界面上显示时,系统渲染文本使用的编码

1 代码-执行

字符串字面值(string literal)分为以下几种(参考String and Character Literals(C++)):

  • 普通字符串”hello”,类型为const char[]
  • 宽字符串L”hello”,类型为const wchar_t[]
  • UTF-8字符串(C++ 11)u8″hello”,类型为const char[]
  • UTF-16字符串(C++ 11)u”hello”,类型为const char16_t[]
  • UTF-32字符串(C++ 11)U”hello”,类型为const char32_t[]

除了普通字符串在编译的时候会保持为代码编码,其他4种字符串在编译的时候会被转换成执行编码。

实验代码:验证字符串编译后的存储方式

#include <iostream>

using namespace std;

template<typename T>
void inspect(T *str)
{
    while (*str != 0)
    {
        cout << (int)*str << ' ';
        ++str;
    }
    cout << endl;
}

int main()
{
    char str[] = "你好";
    wchar_t wstr[] = L"你好";
    char u8str[] = u8"你好";
    char16_t u16str[] = u"你好";
    char32_t u32str[] = U"你好";

    inspect(str);
    inspect(wstr);
    inspect(u8str);
    inspect(u16str);
    inspect(u32str);

    return 0;
}

要让编译器能够正确转换字符串的编码,以及不在处理代码中的非ASCII字符时出错,需要让编译器能够正确识别代码文件的编码。

一些编辑器的默认编码:

  • Visual Studio 2017创建的C++项目默认为UTF-16 LE编码,创建的C++代码文件默认为ANSI编码。
  • Code::Blocks(Windows版)创建的代码文件默认编码为ANSI。
  • Visual Studio Code创建的代码文件默认为UTF-8编码,但是可以通过点击右下角的编码名称,选择“通过编码重新保存”来更改文件编码。

编译器对编码的处理方式:

  • MSVC编译器将没有BOM的文件视作ANSI编码(没有BOM的UTF-8很容易出现语法错误问题),有BOM的文件能够自动识别为对应的UTF编码。
  • GCC编译器对于没有BOM的文件视作UTF-8编码,并且能正确处理有BOM的UTF-8编码文件,但其他有BOM的UTF无法正确识别。通过“-finput-charset=<charset>”参数能够设定输入文件的编码。

2 执行-显示

要让显示出来的时候不乱码,输出文本时使用的编码必需匹配用户计算机上使用的编码才行。Windows命令行的默认显示编码是ANSI,Linux和macOS的命令行默认显示编码是UTF-8。

使用cout输出字符串的话只能输出char[]类型而不能输出wchar_t[]、char16_t[]和char_32[t]类型。要输出wchar_t[],可以使用wcout。在配置正确的情况下,wcout可以自动将你的执行编码转换成用户的显示编码,而如果有用户的显示编码无法显示的字符则会自动忽略。要用wcout正确输出文本,需要将wcout的地区设置为当前的默认地区。

通常来说,以下语句能够让wcout正确工作:

// #include <iostream>
std::wcout.imbue(locale(""));

但若在Linux中使用GCC编译器编译,需要使用:

// #include <iostream>
std::ios_base::sync_with_stdio(false);
std::wcout.imbue(locale(""));

若在Windows中使用MinGW中的GCC编译器,需要注意:MinGW对宽字符的支持不完整(见wide characters | MinGW),如果遇到无法显示的字符(例如在中文Windows中的“₩”)会抛出错误而不是直接忽略。编译器需要使用如下代码才能使wcout正常工作。

// #include <iostream>
// #include <locale>
std::ios_base::sync_with_stdio(false);
setlocale(LC_CTYPE, "");

留言

有想法?请给我们留言!您的留言不会直接显示在网站内。