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

标签 android c++ ios windows directx

我正在开发一个应用程序,其核心代码库将跨平台用于 Windows、iOS 和 Android。

我的问题是:我应该如何在内部表示此应用程序使用的字符串,以便能够在所有三个平台上有效地使用它们?

重要的是要注意,我在 Windows 中大量使用 DirectWrite,其中 API 函数通常期望传递 wchar_t*(顺便说一句。API 文档指出“指向 Unicode 字符数组的指针。”,我不知道这是否意味着它们是 UTF-16 编码)

我看到了三种不同的方法(但是我发现很难掌握以跨平台方式使用 C++ 处理 Unicode 字符串的细节,所以我可能错过了一些重要的概念):

  • 在内部各处使用 std::string(并以 UTF-8 编码存储字符串?),并在 DirectWrite API 需要的地方将它们转换为 wchar_t*(我不知道文本需要什么-尚未处理 Android 和 iOS 的 API)。
  • 内部到处都使用 std::wstring。如果我理解正确,从内存使用的角度来看这不会有效,因为 wchar_t 在 iOS 和 Android 上是 4 个字节(这是否意味着我必须在 Windows 上以 UTF-16 存储字符串,在Android/iOS 上的 UTF-32?)
  • 使用抽象基类为字符串创建抽象,并针对不同平台实现专门的内部存储。

最好的解决方案是什么?顺便问一下,是否有抽象字符串处理的现有跨平台库? (以及读取和序列化 Unicode 字符串)

(更新:删除了关于 char* 和 std::string 的区别的部分。)

最佳答案

我的部分问题来自于我的误解,或者没有完全理解 stringwstring 类在 C++ 中的工作方式(我来自 C# 背景)。 在这个很好的答案中描述了两者的区别和优缺点:std::wstring VS std::string .

string 和 wstring 的工作原理

对我来说,关于 string 和 wstring 类的一个最重要的发现是,从语义上讲,它们并不表示一段编码文本,而只是一个“字符串”字符或 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::字符串)。

从中我们可以看出处理编码的是平台的各种文本处理 API,例如简单的 coutDirectWrite

我的方法

在我的应用程序中,DirectWrite 非常重要,它只接受以 UTF16 编码的字符串(以 wchar_t* 指针的形式)。所以我决定将字符串存储在内存和以 UTF16 编码的文件中。然而,我想要一个跨平台的实现,它可以处理 Windows、Android 和 iOS 上的 UTF16 字符串,这对于 std::wstring 是不可能的,因为它的数据大小(以及它适合的编码) use) 依赖于平台。

为了创建一个跨平台的、严格的 UTF16 字符串类,我将 basic_string 模板化为 2 字节长的数据类型。非常令人惊讶 - 至少对我来说 - 我在网上几乎找不到关于此的信息,我的解决方案基于 this方法。这是代码:

// 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++ 中的字符串处理方面仍然没有太多经验,所以如果我说错了什么,请随时评论/编辑。

关于android - 如何在跨平台(Windows、iOS、Android)C++ 应用程序中表示字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11503145/

相关文章:

java - 如何从 Firebase 中检索 ListView 中的数据

android - Android聚合多条通知时如何动态更改通知消息?

java - 启动只有我的客户端可以连接的 IRC 服务器

c++ - 模板类数据类型

ios - 解析 filterResults findObjects 与 findObjectsInBackgroundWithBlock

ios - 从字符串数组填充 Collection View 单元格标签的最简单方法是什么?

android - SQLite 数据库不显示所有记录

C++ 静态成员函数与 lambda 开销

c++ - boost::asio 异步操作和资源

ios - 设备锁定时的文件安全和 iOS7 中的后台获取