C++ 字符串在安全位置截断utf-8的最佳方法

C++ 字符串在安全位置截断utf-8的最佳方法,c++,string,utf-8,C++,String,Utf 8,我在std::string中有一个有效的utf-8编码字符串。我有字节限制。我想截断字符串并添加。。。在MAX\u SIZE-3-x-其中x是防止剪切utf-8字符的值 是否有函数可以根据最大值确定x,而无需从字符串开头开始 如果您在字符串中有一个位置,并且希望向后查找UTF-8字符的开头(因此是一个有效的剪切位置),那么这是相当容易的 从序列中的最后一个字节开始。如果最后一个字节的前两位是10,则它是UTF-8序列的一部分,因此请继续备份,直到前两位不是10(或直到到达起始位置) UTF-8的

我在std::string中有一个有效的utf-8编码字符串。我有字节限制。我想截断字符串并添加。。。在
MAX\u SIZE-3-x
-其中
x
是防止剪切utf-8字符的值


是否有函数可以根据最大值确定
x
,而无需从字符串开头开始

如果您在字符串中有一个位置,并且希望向后查找UTF-8字符的开头(因此是一个有效的剪切位置),那么这是相当容易的

从序列中的最后一个字节开始。如果最后一个字节的前两位是
10
,则它是UTF-8序列的一部分,因此请继续备份,直到前两位不是
10
(或直到到达起始位置)

UTF-8的工作方式是,根据字节的高位,一个字节可以是三件事之一。如果最上面的位是
0
,则字节是ASCII字符,接下来的7位是Unicode码点值本身。如果最上面的位是
10
,则后面的6位是多字节序列的额外位。但多字节序列的开头在前2位用
11
编码

因此,如果一个字节的顶部位不是
10
,那么它要么是ASCII字符,要么是多字节序列的开头。不管怎样,这都是一个有效的切入点

但是请注意,虽然此算法将在代码点边界处打断字符串,但它忽略Unicode grapheme集群。这意味着组合字符可以从它们组合的基本字符中剔除;例如,字符可能会丢失重音。进行适当的字形聚类分析需要访问Unicode表,该表说明代码点是否为组合字符

但它至少是一个有效的Unicode UTF-8字符串。所以这比大多数人做的要好;)


代码如下所示(在C++14中):

auto-FindCutPosition(常量std::string&str,size\u t max\u size)
{
断言(str.size();
断言(str.size();
最大尺寸-=3;
用于(尺寸位置=最大尺寸;位置>0;--位置)
{
无符号字符字节=static_cast(str[pos]);//完全有效
if(字节&0xC0!=0x80)
返回pos;
}
无符号字符字节=static_cast(str[0]);//完全有效
if(字节&0xC0!=0x80)
返回0;
//如果您的第一个字节甚至不是有效的UTF-8起始点,那么就发生了可怕的事情。
抛出错误的utf8编码的文本(…);
}

实际上,UTF-8编码的字节模式是这样的,确定下一个字符开始的边界几乎是不可能的。我知道数据-我只是不想从请求中迭代以找到哪个是被截断的/如果有的话/字符。问题是我能确定我所在的字符开始的位置吗。这将允许字符串在码点边界处被截断,但它可能会更改字符串中的字符,而不仅仅是将其截断。例如,这可能会导致“简历”变成“简历”。@bames53:公平地说,删减字符会使任何单词变成废话。“haai”到“haa”在语法上也是错误的,只是用荷兰语而不是法语。(不能有后缀-aa)
auto FindCutPosition(const std::string &str, size_t max_size)
{
  assert(str.size() >= max_size, "Make sure stupidity hasn't happened.");
  assert(str.size() > 3, "Make sure stupidity hasn't happened.");
  max_size -= 3;
  for(size_t pos = max_size; pos > 0; --pos)
  {
    unsigned char byte = static_cast<unsigned char>(str[pos]); //Perfectly valid
    if(byte & 0xC0 != 0x80)
      return pos;
  }

  unsigned char byte = static_cast<unsigned char>(str[0]); //Perfectly valid
  if(byte & 0xC0 != 0x80)
    return 0;

  //If your first byte isn't even a valid UTF-8 starting point, then something terrible has happened.
  throw bad_utf8_encoded_text(...);
}