【问题标题】:Extract (first) UTF-8 character from a std::string从 std::string 中提取(第一个)UTF-8 字符
【发布时间】:2014-05-11 13:30:21
【问题描述】:

我需要使用C++ implementation of PHP's mb_strtoupper function 来模仿维基百科的行为。

我的问题是,我只想为函数提供一个 single UTF-8 字符,即 std::string 的第一个字符。

std::string s("äbcdefg");
mb_strtoupper(s[0]); // this obviously can't work with multi-byte characters
mb_strtoupper('ä'); // works

有没有一种有效的方法来只检测/返回字符串的第一个 UTF-8 字符?

【问题讨论】:

  • 除非你有一个库来为你做这件事,否则你最好的选择可能是编写一个函数,通过读取代理字节并编写它来自己读取 UTF-8 字符串的第一个字符转换为单个 32 位整数 - unicode 代码点 - 然后是您的第一个字符。例如,请参阅此答案:stackoverflow.com/questions/2948308/…
  • 对字符进行大小写转换(和其他操作)是个坏主意。组合字符和具有 1:many 映射的字符(例如 'ß' => 'SS')会中断
  • 完全同意米海。然而,在这种情况下,我需要模仿维基百科的内部行为进行分析,所以这是采取的途径。

标签: c++ string utf-8


【解决方案1】:

在 UTF-8 中,第一个字节的高位告诉您有多少后续字节是同一代码点的一部分。

0b0xxxxxxx: this byte is the entire code point
0b10xxxxxx: this byte is a continuation byte - this shouldn't occur at the start of a string
0b110xxxxx: this byte plus the next (which must be a continuation byte) form the code point
0b1110xxxx: this byte plus the next two form the code point
0b11110xxx: this byte plus the next three form the code point

可以假设该模式继续存在,但我认为有效的 UTF-8 不会使用超过四个字节来表示单个代码点。

如果您编写了一个计算前导位数设置为 1 的函数,那么您可以使用它来确定在哪里拆分字节序列以隔离第一个逻辑代码点,假设输入是有效的 UTF- 8.如果您想针对无效的 UTF-8 进行加固,则必须编写更多代码。

另一种方法是利用连续字节始终与模式0b10xxxxxx 匹配这一事实,因此您获取第一个字节,然后只要下一个字节与该模式匹配就继续获取字节。

std::size_t GetFirst(const std::string &text) {
  if (text.empty()) return 0;
  std::size_t length = 1;
  while ((text[length] & 0b11000000) == 0b10000000) {
    ++length;
  }
  return length;
}

对于许多语言,单个代码点通常映射到单个字符。但人们所认为的单个字符可能更接近于 Unicode 所称的字素簇,即一个或多个代码点组合起来产生一个字形。

在您的示例中,ä 可以用不同的方式表示:它可以是单个代码点 U+00E4 LATIN SMALL LETTER A WITH DIAERESIS 它可以是 U+0061 LATIN SMALL LETTER AU+0308 COMBINING DIAERESIS 的组合。幸运的是,只需选择第一个代码点就可以实现首字母大写的目标。

如果你真的需要第一个 grapheme cluster,你必须超越第一个代码点来查看下一个(s)是否与它结合。对于许多语言,知道哪些代码点是“非间距”或“组合”或变体选择器就足够了。对于一些复杂的脚本(例如,韩文?),您可能需要求助于这个Unicode Consortium technical report

【讨论】:

    【解决方案2】:

    图书馆str.h

    #include <iostream>
    #include "str.h"
    
    int main (){
        std::string text = "äbcdefg";
        std::string str = str::substr(text, 0, 1); // Return:~ ä
        std::cout << str << std::endl;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-11-10
      • 2012-10-25
      • 2017-01-26
      • 1970-01-01
      • 2019-11-21
      • 1970-01-01
      • 2014-10-13
      • 1970-01-01
      • 2013-09-26
      相关资源
      最近更新 更多