如何获得 std::string 的准确长度?

Posted

技术标签:

【中文标题】如何获得 std::string 的准确长度?【英文标题】:How to get the accurate length of a std::string? 【发布时间】:2015-07-27 11:53:52 【问题描述】:

我正在使用此代码修剪一个长 std::string 以将其放入文本容器中。

std::string AppDelegate::getTrimmedStringWithRange(std::string text, int range)

    if (text.length() > range)
    
        std::string str(text,0,range-3);
        return str.append("...");
    
    return text;

但是对于像HINDI "हिन्दी" 这样的其他语言,std::string 的长度是错误的。

我的问题是如何在所有测试用例中检索 std::string 的准确长度。

谢谢

【问题讨论】:

std::string 仅支持 ASCII。您可能需要std::wstring 或类似的数据结构 我可以将 std::string 更改为 std::wstring 吗?反之亦然? 是的,看到这个:***.com/questions/2573834/… @AndyG: std::string 不支持任何特定的编码。它只存储字节。它完全能够存储非 ascii 字符串。例如 UTF-8。 @AndyG,我不知道那个字符串的确切长度,它看起来像“3”,如果你使用 wstring,长度是 6。正如 Benjamin Lindley 指出的那样,只有 std::string存储字节,内部表示取决于您的设置。 【参考方案1】:

假设您使用的是 UTF-8,您可以将字符串转换为简单的(哈哈!)Unicode 并计算字符数。我从rosettacode 获取了这个例子。

#include <iostream>
#include <codecvt>
int main()

    std::string utf8 = "\x7a\xc3\x9f\xe6\xb0\xb4\xf0\x9d\x84\x8b"; // U+007a, U+00df, U+6c34, U+1d10b
    std::cout << "Byte length: " << utf8.size() << '\n';
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
    std::cout << "Character length: " << conv.from_bytes(utf8).size() << '\n';

【讨论】:

这将产生 Unicode 代码点中字符串的长度,但不会产生字符串的显示大小,因为一些 Unicode 字符的长度为零(特别是组合字符,如变音符号),而另一些则有长度两个在固定宽度的控制台上。 (如果输出不是固定宽度的字体,那么情况就完全不同了,很明显。)在 Posix 中,您可以使用 wcswidth,但这可能适用于 C++,也可能不适用于 C++。【参考方案2】:

std::string 的长度没有“错误”;你只是误解了它的意思。 std::string 以您选择的编码存储字节,而不是“字符”。它兴高采烈地不知道那一层。因此,std::string 的长度就是它包含的字节数。

要计算此类“字符”,您需要一个支持分析您选择的编码的库。

只有当您选择的编码与 ASCII 兼容时,您才能计算字节数并使用它。

【讨论】:

IIRC,有多种正确的方法来计算 Unicode 中的“字符”。对于每个,您可能想要计算“字素”而不是代码点。所以即使你坚持使用 UTF-8,你也需要考虑一下你想要计算什么样的东西。 (同样,据我所知,除了 UTF-8 方案,我真的知道的很少)【参考方案3】:

如 cmets 中所述,长度将返回以utf8 编码的字符串的字节数。在这种多字节编码中,非 ascii 字符被编码为 2 到 6 个字节,因此您的 utf8 字符串长度会显得比 unicode 字母的实际数量更长。

解决方案 1

如果你有很多长字符串,你可以把它们保存在 utf8 中。 utf8 编码使得找出额外的多字节字符变得相对容易:它们都以二进制的 10xxxxxx 开头。所以计算这些额外字节的数量,并从字符串长度中减去它

cout << "Bytes: " << s.length() << endl;
cout << "Unicode length " << (s.length() - count_if(s.begin(), s.end(), [](char c)->bool  return (c & 0xC0) == 0x80; )) << endl;

解决方案 2

如果需要更多处理而不仅仅是计算长度,您可以考虑在标准库中使用wstring_convert::from_bytes() 将您的字符串转换为 wstring。 wstring 的长度应该是您所期望的。

wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> cv;
wstring w = cv.from_bytes(s);
cout << "Unicode length " << w.length() << endl;

注意: linux上的wstring是基于32位wchar_t的,一个这样的宽字符可以包含所有的unicode字符集。所以这是完美的。然而,在 Windows 上,wchar_t 只有 16 位,因此某些字符可能仍需要多字编码。幸运的是,所有hindi characters 都在 U+0000 到 U+D7FF 的范围内,可以在一个 16 位字上进行编码。所以也应该没问题。

【讨论】:

你怎么知道是UTF-8? @LightnessRacesinOrbit 好问题。最佳猜测:它带有 cocos2d-x 标签,supported platforms 都符合 unicode 或 UCS16。在这种情况下,以 utf8 编码存储 unicode 似乎比其他多字节编码更有可能

以上是关于如何获得 std::string 的准确长度?的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 中的 std::string 获取字节

C++ std::string::size()函数(返回字符串的长度,以字节为单位)(与std::string::length()函数相同)

根据缓冲区长度将空终止字符数组复制到 std::string

C++ 从可变长度参数列表中提取 std::string

对上一篇文章的澄清,Geohashing 字符串长度和准确性?

从文件中读取未知长度的 int 数组