0%

C++的宽字符处理

1. 背景介绍

C++在处理字符串时,常常会碰到宽字符(中日韩等非ACSII码字符),默认的标准库 std::string 处理的仅是 char 类型的字符串。通常,在 Unix/Linux 系统中,字符串是 UTF-8 编码的,此时,std::string 中存储的即为 UTF-8 编码后的结果。例如:“你好啊,Stick”,这句话中有 9 个字符,但实际存储在 std::string 中的内容为:"\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x95\x8a\xef\xbc\x8cStick" 实际长度为 17. 那么如何才能将 UTF-8 的编码转换为实际的 Unicode 编码呢?

2. C++ 中 UTF-8 转换为 Unicode 的方法及优缺点介绍

  1. std::mbstowcs
    该函数定义在 <cstdlib> 头文件中,是一个 C 函数,通常配合 <clocale> 头文件中的 std::setlocale 使用,设置当前系统编码,将当前编码的字符串,转换为宽字符串。

    1
    2
    3
    4
    std::setlocale(LC_ALL, "en_US.utf8");
    char * str = "\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x95\x8a\xef\xbc\x8cStick";
    wchar_t wstr[10];
    std::mbstowcs(wstr, str, 9);

    该方法,在gcc 4以及更高版本中均可使用。缺点是,跨平台可能会出现问题。例如,同样的代码在 Linux 下运行良好。到了 Windows 下的 MinGW64 下就会出现问题。此外,该方法只能转换一般的宽字符,对于一些超宽字符如“𠀳”(U+20033) 则无法表示,需要使用 char32_t 类型才能表示。

  2. std::wstring_convert
    该函数定义在 <locale> 头文件中,通常配合 <codecvt> 头文件的 std::codecvt_utf8 函数使用。

    1
    2
    3
    4
    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convertor;
    std::string str = "\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x95\x8a\xef\xbc\x8cStick"
    std::wstring wstr = convertor.from_bytes(str);
    std::string nstr = convertor.to_bytes(wstr);

    该方法,实用性很强,能够跨平台使用(MinGW64 测试正常),而且针对超宽字符也能够顺利转换,只需要将上述代码中的 wchar_t 修改为 char32_tstd::wstring 修改为 std::u32string 即可。缺点是对 gcc 版本有要求,必须为 gcc 5.0 及以上版本才可以,且 -std 参数必须设置为 c++11 及以上版本才可。

  3. 根据 UTF-8 或其他编码规则,手动书写转换代码。优点是无需考虑编译器版本,无需考虑平台限制。缺点是需要熟悉各种转换规则,增大开发难度。但是也能一劳永逸。

3. 总结

本文未对提及的方法进行速度上的测试,仅对功能性和实用性进行了测试。从这一角度出发,推荐使用 std::wstring_convert。如果是需要维护一些老的工程代码,必须使用 gcc 4 版本的编译器,则仍旧使用原来的方案或者采用手动书写转换代码的方案,若配合上编译选项,则很容易的将代码进行高低版本的兼容。

您的支持将鼓励我继续创作!