C ++ Unicode:字节,代码点和字形

时间:2017-01-17 16:26:13

标签: c++ codepoint rune grapheme

所以,我正在构建脚本语言,我的目标之一是方便的字符串操作。我在C ++中尝试了一些想法。

  • 字符串作为字节和自由函数的序列,返回包含代码点索引的向量。
  • 一个包装类,它结合了一个字符串和一个包含索引的向量。

这两个想法都有问题,那个问题是,我该怎么回事。它不可能是一个字符,如果它是一个字符串,那将是浪费的空间。

我最终围绕一个恰好4个字节的char数组创建了一个包装类:一个在内存中只有4个字节的字符串,不多也不少。

创建这个类之后,我觉得很想把它包装在另一个类的std::vector中并从那里构建,从而形成一个字符串类型的代码点。我不知道这是不是一个好方法,它最终会更方便,但最终会浪费更多空间。

所以,在发布一些代码之前,这里有一个更有条理的想法列表。

  • 我的字符类型不是字节,也不是字形,而是代码点。我把它命名为Go语言中的符文。
  • 作为一系列分解符文的字符串,从而对索引进行索引和切片。
  • 因为符文现在是一个类而不是一个基元,所以可以使用检测unicode空格的方法进行扩展:mysring[0].is_whitespace()
  • 我仍然不知道如何处理字素。
好奇的事实!我构建符文类原型的方式奇怪的是它总是以UTF8打印。因为我的符文不是int32,而是4字节的字符串,所以最终会有一些有趣的属性。

我的代码:

class rune {
    char data[4] {};
public:
    rune(char c) {
        data[0] = c;
    }

    // This constructor needs a string, a position and an offset!
    rune(std::string const & s, size_t p, size_t n) {
        for (size_t i = 0; i < n; ++i) {
            data[i] = s[p + i];
        }
    }

    void swap(rune & other) {
        rune t = *this;
        *this = other;
        other = t;
    }

    // Output as UTF8!
    friend std::ostream & operator <<(std::ostream & output, rune input) {
        for (size_t i = 0; i < 4; ++i) {
            if (input.data[i] == '\0') {
                return output;
            }
            output << input.data[i];
        }
        return output;
    }
};

错误处理想法:

我不喜欢在C ++中使用异常。我的想法是,如果构造函数失败,则将符文初始化为4 '\0',然后显式重载bool运算符,如果运行的第一个字节恰好是'\0'则返回false。简单易用。

那么,想法?意见?不同的方法?

即使我的符文字符串很多,至少我有一个符文类型。小而快速复制。 :)

1 个答案:

答案 0 :(得分:0)

听起来你正试图重新发明轮子。

当然,您需要考虑两种方式来考虑文本:

  • 作为代码点数组
  • 作为编码的字节数组。

在某些代码库中,这两个表示形式是相同的(并且所有编码基本上都是char32_tunsigned int的数组)。在一些(我倾向于说“最”但不引用我),编码的字节数组将使用UTF-8,其中代码点在被放入数据结构之前被转换为可变长度的字节

当然,许多代码库完全忽略unicode并将其数据存储在ASCII中。我不推荐。

出于您的目的,虽然编写一个类来“包裹”您的数据是有意义的(虽然我不会将其称为rune,但我可能只称它为codepoint }),你会想到你的语义。

  • 您可以(并且可能应该)将所有std::string视为UTF-8编码字符串,并将此作为处理文字的默认界面。对于大多数外部接口来说是安全的 - 唯一一次失败就是在与UTF-16输入接口时,你可以为它编写极端情况 - 它将为你节省大部分内存,同时仍然遵守常见的字符串约定(它是按字典顺序可比,这是最大的一个。)。
  • 如果您需要以代码点形式处理数据,那么您将需要编写一个名为codepoint的结构(或类),其中包含以下有用的函数和构造函数
    • 虽然我不得不编写以代码点形式处理文本的代码(特别是字体渲染器),但这可能应该如何存储文本。当您不断地与UTF-8或ASCII编码的字符串进行比较时,将文本存储为代码点会导致问题。

代码:

struct codepoint {
    char32_t val;
    codepoint(char32_t _val = 0) : val(_val) {}
    codepoint(std::string const& s);
    codepoint(std::string::const_iterator begin, std::string::const_iterator end);
    //I don't know the UTF-8→codepoint conversion off-hand. There are lots of places
    //online that show how to do this

    std::string to_utf8() const;
    //Again, look up an algorithm. They're not *too* complicated.
    void append_to_string_as_utf8(std::string & s) const;
    //This might be more performant if you're trying to reduce how many dynamic memory 
    //allocations you're making.

    //codepoint(std::wstring const& s);
    //std::wstring to_utf16() const;
    //void append_to_string_as_utf16(std::wstring & s) const;

    //Anything else you need, equality operator, comparison operator, etc.
};