【问题标题】:How to remove the last character of a UTF-8 string in C++?如何在 C++ 中删除 UTF-8 字符串的最后一个字符?
【发布时间】:2016-10-04 01:12:19
【问题描述】:

文本存储在std::string中。

如果文本是8位ASCII,那真的很简单:

text.pop_back();

但是如果是 UTF-8 文本呢?
据我所知,标准库中没有我可以使用的 UTF-8 相关函数。

【问题讨论】:

  • 你说的“字符”是什么意思?代码单位?码点?字素簇?
  • @polfosol:std::basic_string 应该如何提供帮助?您需要一个 UTF 库来操作 UTF 文本编码。
  • 要么找一个不错的 utf8 库,要么学习 utf8 结构并自己实现
  • IterAtor 我认为你错过了@IInspectable 的观点。在 unicode 中,“字符”是一个模棱两可的概念,与编码方法(UTF8、UTF16 等)无关。这就是为什么他们要求澄清你所说的“性格”是什么意思。预警;标准库基本上不支持 unicode。你将不得不求助于一个 unicode 特定的库。
  • @self:我相信你严重低估了 Unicode 的复杂性。 U+0301(结合重音)呢?还是 U+200C(零宽度非连接器)? U+FB00 (latin small ligature ff) 怎么样?每一个都被分配了一个代码点,并且没有一个被认为是一个字符。

标签: c++ string c++11 unicode utf-8


【解决方案1】:

如果您要使用UTF-8,您真的需要UTF-8 Library。但是对于这项任务,我认为这样的事情可能就足够了:

void pop_back_utf8(std::string& utf8)
{
    if(utf8.empty())
        return;

    auto cp = utf8.data() + utf8.size();
    while(--cp >= utf8.data() && ((*cp & 0b10000000) && !(*cp & 0b01000000))) {}
    if(cp >= utf8.data())
        utf8.resize(cp - utf8.data());
}

int main()
{
    std::string s = "κόσμε";

    while(!s.empty())
    {
        std::cout << s << '\n';
        pop_back_utf8(s);
    }
}

输出:

κόσμε
κόσμ
κόσ
κό
κ

它依赖于UTF-8 Encoding 有一个起始字节后跟几个连续字节这一事实。这些连续字节可以使用提供的按位运算符检测。

【讨论】:

  • 如果您使用组合字形,这仍然是一个问题:κόσÄÄμε κόσÄÄμ κόσÄÄ κόσÄA κόσÄ κόσ κό κ
  • @Taywee “组合字形”是什么意思?它似乎与该字符串一起工作正常。
  • 他的意思是“组合字符”。此代码可能仅删除最后一个组合字符(想想变音符号),但不会删除基本字符。结果将是一个可能失去其意义的字符串,而不是从字符串中删除最后一个字素簇(又名。读者会认为是“字符”)。
  • @CoryNelson 我刚刚阅读了有关它们的信息。好吧,我认为他们似乎在不同的层面上运作。弹出代码点基本上是UTF-8,而组合字符似乎是应用程序可能使用或可能不使用的约定。我认为它可能由应用程序级别决定弹出多少代码点来删除给定的字形。
  • @CoryNelson FWIW,删除最后一个字素簇也可能导致字符串失去意义。这完全取决于这里所讨论的含义。
【解决方案2】:

您可以做的是弹出字符,直到到达代码点的前导字节。 UTF8 中代码点的前导字节是0xxxxxxx11xxxxxx 模式之一,所有非前导字节都是10xxxxxx 格式。这意味着您可以检查第一位和第二位以确定您是否有前导字节。

bool is_leading_utf8_byte(char c) {
    auto first_bit_set = (c & 0x80) != 0;
    auto second_bit_set = (c & 0X40) != 0;
    return !first_bit_set || second_bit_set;
}

void pop_utf8(std::string& x) {
    while (!is_leading_utf8_byte(x.back()))
        x.pop_back();
    x.pop_back();
}

这当然不会进行错误检查,并假定您的字符串是有效的 utf-8。

【讨论】:

  • 为什么first_bit_setsecond_bit_set 是auto,而不是bool?
  • 这适用于最简单的情况。请注意,如果您使用组合字符,这将失败,因为它只会从其字形中删除组合字符,而不是完全删除字形。
  • @IterAtor:因为我总是使用 auto。你不必这样做。
  • 您可以将 is_leading_utf8_byte 简化为 return (c &amp; 0xC0) != 0x80 以测试它不是尾随字节 (10xxxxxx)。
猜你喜欢
  • 2013-10-14
  • 2011-04-14
  • 2016-10-11
  • 1970-01-01
  • 2018-01-23
  • 2011-01-19
  • 1970-01-01
  • 1970-01-01
  • 2019-04-30
相关资源
最近更新 更多