代码之家  ›  专栏  ›  技术社区  ›  sorin

C++环境下处理Unicode字符串的最佳多平台方法是什么?

  •  18
  • sorin  · 技术社区  · 15 年前

    我知道关于stackoverflow已经有几个问题 std::string 对战 std::wstring 或者类似的,但没有人提出完整的解决方案。

    为了得到一个好的答案,我应该定义以下要求:

    • 多平台 用法,必须适用于Windows、OS X和Linux
    • 最小的努力 与平台特定的Unicode字符串的转换 喜欢 CFStringRef , wchar_t * , char* 如UTF-8或其他类型,如OS API所需。备注:我不需要代码页转换支持,因为我希望在所有支持的操作系统上只使用Unicode兼容的函数。
    • 如果需要外部库,则此库应该 开放源代码 在一个非常自由的许可下,如BSD,但不是LGPL。
    • 能够使用 打印格式 语法或类似。
    • 字符串分配/释放的简单方法
    • 性能不是很重要,因为我假设Unicode字符串只用于应用程序UI。
    • 有一些例子值得赞赏

    我真的很感激 每个答案一个建议的解决方案 通过这样做,人们可以投票选择他们喜欢的替代方案。如果你有多个选择,只需添加另一个答案。

    请指出一些 是为你工作的吗? .

    相关问题:

    5 回复  |  直到 15 年前
        1
  •  5
  •   Klaim    15 年前

    和亚当·罗森菲尔德的答案一样(+1),但我用 UTFCPP 相反。

        2
  •  7
  •   Adam Rosenfield    15 年前

    我强烈建议在应用程序内部使用utf-8,使用常规的old char* std::string 用于数据存储。对于使用不同编码(ASCII、UTF-16等)的API的接口,我建议使用 libiconv ,根据LGPL授权。

    示例用法:

    class TempWstring
    {
    public:
      TempWstring(const char *str)
      {
        assert(sUTF8toUTF16 != (iconv_t)-1);
        size_t inBytesLeft = strlen(str);
        size_t outBytesLeft = 2 * (inBytesLeft + 1);  // worst case
        mStr = new char[outBytesLeft];
        char *outBuf = mStr;
        int result = iconv(sUTF8toUTF16, &str, &inBytesLeft, &outBuf, &outBytesLeft);
        assert(result == 0 && inBytesLeft == 0);
      }
    
      ~TempWstring()
      {
        delete [] mStr;
      }
    
      const wchar_t *Str() const { return (wchar_t *)mStr; }
    
      static void Init()
      {
        sUTF8toUTF16 = iconv_open("UTF-16LE", "UTF-8");
        assert(sUTF8toUTF16 != (iconv_t)-1);
      }
    
      static void Shutdown()
      {
        int err = iconv_close(sUTF8toUTF16);
        assert(err == 0);
      }
    
    private:
      char *mStr;
    
      static iconv_t sUTF8toUTF16;
    };
    
    iconv_t TempWstring::sUTF8toUTF16 = (iconv_t)-1;
    
    // At program startup:
    TempWstring::Init();
    
    // At program termination:
    TempWstring::Shutdown();
    
    // Now, to convert a UTF-8 string to a UTF-16 string, just do this:
    TempWstring x("Entr\xc3\xa9""e");  // "Entrée"
    const wchar_t *ws = x.Str();  // valid until x goes out of scope
    
    // A less contrived example:
    HWND hwnd = CreateWindowW(L"class name",
                              TempWstring("UTF-8 window title").Str(),
                              dwStyle, x, y, width, height, parent, menu, hInstance, lpParam);
    
        3
  •  2
  •   Jon Reid    15 年前

    我最近在一个项目中决定使用std::wstring来进行跨平台项目,因为“宽字符串是Unicode,对吗?”这导致了许多头痛:

    • wstring中的标量值有多大?答:这取决于编译器的实现。在Visual Studio(Win)中,它是16位。但在xcode(mac)中,它是32位。
    • 这导致了一个不幸的决定,即使用UTF-16进行有线通信。但是哪种UTF-16呢?有两种:utf-16be(big endian)和utf16-le(little endian)。不清楚这一点会导致更多的错误。

    当您使用特定于平台的代码时,使用平台的本机表示与它的API进行通信是有意义的。但是对于跨平台共享或平台间通信的任何代码,都要避免所有的歧义并使用UTF-8。

        4
  •  1
  •   Mihai Nita    15 年前

    经验法则:使用原生平台unicode表单进行处理(utf-16或utf-32),使用utf-8进行数据交换(通信、存储)。

    如果所有本地API都使用UTF-16(例如在Windows中),将字符串设置为UTF-8意味着您必须将所有输入转换为UTF-16,请调用win API,然后将答案转换为UTF-8。相当痛苦。

    但是,如果主要问题是UI,那么字符串就是简单的问题。 更困难的是UI框架。 为此,我推荐wxwidgets( http://www.wxWidgets.org )支持许多平台,成熟(17年,仍然非常活跃),本地小部件,Unicode,自由许可证。

        5
  •  1
  •   Tobias Langner    15 年前

    我会在内存中使用UTF16表示,在硬盘或电线上使用UTF-8或16表示。主要原因:utf16对于每个“字母”都有固定的大小。这简化了处理字符串(搜索、更换零件等)时的许多职责。

    UTF-8的唯一原因是“西文/拉丁文”字母的内存使用量减少。您可以使用此表示法通过网络进行磁盘存储或传输。它还有一个好处,即在加载/保存到磁盘/线时,您不必担心字节顺序。

    考虑到这些原因,我会在内部使用std::wstring,或者-如果您的GUI库提供了一个宽字符串,那么使用它(比如qt中的qstring)。对于磁盘存储,我将为平台API编写一个独立于平台的小型包装器。或者,如果unicode.org中有可用于此转换的平台依赖代码,我也会签出它。


    澄清:朝鲜语/日语字母不是西方/拉丁语。日语是以汉字为例的。这就是我提到拉丁字符集的原因。


    对于不是1个字符/2字节的UTF-16。此假设仅适用于位于基本多语言平面上的字符(请参见: http://en.wikipedia.org/wiki/UTF16 )不过,大多数UTF-16用户仍然认为所有字符都在BMP上。如果您的应用程序不能保证这一点,您可以切换到utf32或切换到utf8。

    在很多API(例如Windows、Qt、Java、.NET、WxWIDGet)中,仍然使用UTF16的原因。