从std :: string中提取(第一个)UTF-8字符

时间:2014-04-01 00:24:13

标签: c++ string utf-8

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

我的问题是,我想只为该函数提供单个 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字符?

2 个答案:

答案 0 :(得分:4)

[已编辑以区分代码点和字符以及字形集群。]

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

0b0xxxxxx: this byte is the entire code point
0b10xxxxx: this byte is a continuation byte--this shouldn't occur at the start of a string
0b110xxxx: this byte plus the next (which must be a continuation byte) form the code point
0b1110xxx: this byte plus the next two form the code point
0b11110xx: 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 它可以是{{1}的组合}和U+0061 LATIN SMALL LETTER A。幸运的是,只需选择第一个代码点就可以实现您的目标,即将首字母大写。

如果您确实需要第一个字形集群,则必须超越第一个代码点以查看下一个代码点是否与之结合。对于许多语言来说,足以知道哪些代码点是"非间距"或"合并"或变体选择器。对于某些复杂的脚本(例如,Hangul?),您可能需要转到此Unicode Consortium technical report

答案 1 :(得分:0)

图书馆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;
}