C++Unicode:字节、代码点和图形



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

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

这两个想法都有问题,问题是,我应该返回什么。它不可能是一个字符,如果它是一个字符串,就会浪费空间。

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

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

因此,在发布一些代码之前,这里有一个更有组织的想法列表。

  • 我的字符类型不是字节,也不是字形,而是代码点。我把它命名为一个符文,就像围棋语言中的符文一样
  • 一个字符串作为一系列分解符文,从而使索引和切片O1
  • 因为符文现在是一个类而不是基元,所以可以使用检测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] == '') {
return output;
}
output << input.data[i];
}
return output;
}
};

错误处理思路:

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

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

即使我的符文字符串太多,至少我有一个符文类型。复制起来又小又快。:)

听起来像是在重新发明轮子。

当然,你需要用两种方式来思考文本:

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

在一些代码库中,这两种表示是相同的(并且所有编码基本上都是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.
};

最新更新