如何在跨平台(Windows、iOS、Android)的C++应用程序中表示字符串?

3
我正在开发一个应用程序,其核心代码库将跨平台运行于Windows、iOS和Android。我的问题是:我应该如何在所有三个平台上有效地使用应用程序所使用的字符串?需要注意的是,在Windows中我会大量使用DirectWrite,该API函数通常期望传递wchar_t*(顺便说一下,API文档说明“指向Unicode字符数组的指针”,我不知道这是否意味着它们采用UTF-16编码)。我看到有三种不同的方法(然而,我发现以跨平台方式处理C++中的Unicode字符串细节很难掌握,因此可能会错过一些重要的概念):
1. 在内部到处使用std::string(并将字符串存储为UTF-8编码?),并在需要DirectWrite API时将它们转换为wchar_t*(我还不知道Android和iOS的文本处理API需要什么)。 2. 在内部到处使用std::wstring。如果我理解正确,从内存使用的角度来看,这并不是一种有效的方法,因为在iOS和Android上,wchar_t占用4个字节(这是否意味着我必须在Windows上使用UTF-16存储字符串,在Android/iOS上使用UTF-32存储字符串?) 3. 为字符串创建抽象类,并具体实现不同平台的内部存储。
哪种方法最好?顺便说一下,是否有现有的跨平台库抽象字符串处理(以及读取和序列化Unicode字符串)?

1
(或者std :: string?有区别吗)嗯,在使用C ++时,几乎没有很好的理由使用char *来表示文本字符串:] https://dev59.com/YHRA5IYBdhLWcg3w2x2W - stijn
3个回答

6
我的问题的一部分来自于我对C++中stringwstring类如何工作的误解或不完全理解(我来自C#背景)。 这个伟大的答案已经描述了这两者之间的区别、优缺点:std::wstring VS std::string

string和wstring的工作原理

对我而言,关于string和wstring类的最重要发现是它们在语义上并不代表一段编码文本,而只是char或wchar_t的“字符串”。它们更像是一个带有一些特定于字符串的操作(如append和substr)的简单数据数组,而不是表示文本。它们都不知道任何类型的字符串编码,它们将每个char或wchar_t元素单独处理为单个字符。

编码

但是,在大多数系统上,如果您使用特殊字符从字符串字面量创建字符串,例如:
std::string s("ű");
ű 这个字符在内存中会被表示为多个字节,但这与 std::string 类无关,这是编译器的一个特性,它可以使用 UTF8 编码字符串字面量(不是每个编译器都支持)。 (以 L 为前缀的字符串字面量将使用 wchar_t-s 表示,具体取决于编译器是UTF16、UTF32还是其他格式)。
因此,字符串 "ű" 在内存中的表示形式为两个字节:0xC5 0xB1,而 std::string 类并不知道这两个字节语义上意味着 UTF8 中的一个字符(一个 Unicode 代码点),因此出现了以下示例代码:
std::string s("ű");
std::cout << s.length() << std::endl;
std::cout << s.substr(0, 1);

根据编译器的不同,下面是可能的结果(某些编译器可能不会将字符串字面量视为UTF8,有些编译器依赖于源文件的编码):

2
�

size()函数返回2,因为std::string只知道它存储了两个字节(两个字符)。substr函数也是“原始”的,它返回一个包含单个字符0xC5的字符串,该字符显示为�,因为它不是有效的UTF8字符(但这并不影响std::string)。
从这里我们可以看出,处理编码的是平台的各种文本处理API,比如简单的coutDirectWrite
我的方法:
在我的应用程序中,DirectWrite非常重要,它只接受以UTF16编码的字符串(以wchar_t*指针形式)。因此,我决定将字符串在内存和文件中都以UTF16编码存储。然而,我希望创建一个跨平台的实现,可以处理Windows、Android和iOS上的UTF16字符串,但使用std::wstring是不可能的,因为它的数据大小(以及适合使用的编码)取决于平台。
为了创建一个跨平台、严格的UTF16字符串类,我在一个长度为2个字节的数据类型上对basic_string进行了模板化。令人惊讶的是,至少对我来说,在网上几乎没有关于这个的信息,我基于这个方法来实现。以下是代码:
// Define this on every platform to be 16 bytes!
typedef unsigned short char16;

struct char16_traits
{
    typedef char16 _E;
    typedef _E char_type;
    typedef int int_type;
    typedef std::streampos pos_type;
    typedef std::streamoff off_type;
    typedef std::mbstate_t state_type;
    static void assign(_E& _X, const _E& _Y)
    {_X = _Y; }
    static bool eq(const _E& _X, const _E& _Y)
    {return (_X == _Y); }
    static bool lt(const _E& _X, const _E& _Y)
    {return (_X < _Y); }
    static int compare(const _E *_U, const _E *_V, size_t _N)
    {return (memcmp(_U, _V, _N * 2)); }
    static size_t length(const _E *_U)
    {
        size_t count = 0;
        while(_U[count] != 0)
        {
            count++;
        }
        return count;
    }
    static _E * copy(_E *_U, const _E *_V, size_t _N)
    {return ((_E *)memcpy(_U, _V, _N * 2)); }
    static const _E * find(const _E *_U, size_t _N, const _E& _C)
    {
        for(int i = 0; i < _N; ++i) {
            if(_U[i] == _C) {
                return &_U[i];
            }
        }
        return 0;
    }
    static _E * move(_E *_U, const _E *_V, size_t _N)
    {return ((_E *)memmove(_U, _V, _N * 2)); }
    static _E * assign(_E *_U, size_t _N, const _E& _C)
    {
        for(size_t i = 0; i < _N; ++i) {
            assign(_U[i], _C);
        }
        return _U;
    }
    static _E to_char_type(const int_type& _C)
    {return ((_E)_C); }
    static int_type to_int_type(const _E& _C)
    {return ((int_type)(_C)); }
    static bool eq_int_type(const int_type& _X, const int_type& _Y)
    {return (_X == _Y); }
    static int_type eof()
    {return (EOF); }
    static int_type not_eof(const int_type& _C)
    {return (_C != eof() ? _C : !eof()); }
};

typedef std::basic_string<unsigned short, char16_traits> utf16string;

字符串使用上述类存储,原始的UTF16数据传递给不同平台的特定API函数,目前所有平台似乎都支持UTF16编码。
实现可能不完美,但是append、substr和size函数似乎能够正常工作。我对C++中的字符串处理还没有很多经验,如果我说错了什么,请随时评论/编辑。


2
std::string和char*之间的区别在于,std::string类使用了C++的特性,而char*没有。std::string是一个字符的容器类,定义了方便的方法来使用它,而char*是指向某些内存的指针,你可以使用它。
如果你正在寻找一些平台无关的基础类,我会指向QString。这是Qt库的一部分,旨在实现C++的跨平台独立实现。{{link2:它也是开源的},所以你可以使用它来了解其他人如何实现平台无关的字符串。文档也非常好。

谢谢,我会研究一下 QString。 - Mark Vincze
它还具有内置的国际化支持。如果您还不了解该框架,我建议您尝试一下。对于移动开发的支持不断增长。阅读此简短介绍:点击这里 - HaMster

1

在每个平台上实现一个抽象类以不同的方式表示似乎是一个不好的想法。额外的工作需要在每个平台上进行实现和测试,并且会增加比仅使用std::wstring更多的开销(当然,您可以通过不使用抽象类而使用#ifdefs来切换实现来抵消开销,但仍需要额外的工作)。

无论是在任何地方都使用std::string还是std::wstring似乎都是正确的方法,实现一些实用程序函数来将您选择的字符串转换为系统相关格式,您就不会有问题了。我正在开发一个多平台项目,该项目已在iOS、Windows、Linux和Mac上运行,在这个项目中,我使用了多字节的std::string,并没有遇到太多问题,从未使用过std::wstring,但我不认为它不能工作。


这意味着你始终以特定编码(UTF-8?)存储字符串,如果需要使用的API需要,你将转换字符串为其他格式或编码。或者你所使用的所有API都需要相同的编码? - Mark Vincze
1
@Mark 我总是将字符串存储为UTF8,并从UTF8转换为系统所需的格式。 - fbafelipe

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