如何获取std::string中字符的数量?

132

我应该如何在C++中获取字符串的字符数量?


1
你正在处理什么类型的字符串?std::string?cstring?以空字符结尾的字符串? - Steve Rowe
最保险的方法是通过for循环遍历它并自己计算字符数。 - Krythic
12个回答

191
如果你正在使用 std::string,调用 length() 函数。
std::string str = "hello";
std::cout << str << ":" << str.length();
// Outputs "hello:5"

如果您正在使用C字符串,请调用strlen()函数。

const char *str = "hello";
std::cout << str << ":" << strlen(str);
// Outputs "hello:5"

或者,如果你偏爱使用帕斯卡风格的字符串(或者像Joel Spolsky在这篇文章中所说的,“f ***** strings”是指它们具有尾随的NULL),只需引用第一个字符。

const char *str = "\005hello";
std::cout << str + 1 << ":" << *str;
// Outputs "hello:5"

8
谁会理智地使用Pascal风格的字符串? - Xarn
27
length()方法返回的是字节数而不是字符数吗? - user1052335
2
对于std::string来说,这是相同的(除非您正在存储可变长度编码或使用std::string存储多字节字符)。如果您这样做,您将无法从std库中获得太多帮助,因此您可以处理自己的strlen。对于wstring、u16string和u32string,它返回字符数,而不是字节数。(再次强调,如果您在其中任何一个中使用可变长度编码,则必须自己滚动strlen)。 - Eclipse
4
在这个高评分的回答中提到返回类型 std::size_t 会增加价值。 - chux - Reinstate Monica
3
@Eclipse +1 支持 *f**ed 字符串 - Sнаđошƒаӽ

23

处理C++字符串(std::string)时,您需要使用length()size()。两者都应该提供相同的值。然而,在处理C风格的字符串时,您需要使用strlen()

#include <iostream>
#include <string.h>

int main(int argc, char **argv)
{
   std::string str = "Hello!";
   const char *otherstr = "Hello!"; // C-Style string
   std::cout << str.size() << std::endl;
   std::cout << str.length() << std::endl;
   std::cout << strlen(otherstr) << std::endl; // C way for string length
   std::cout << strlen(str.c_str()) << std::endl; // convert C++ string to C-string then call strlen
   return 0;
}

输出结果:

6
6
6
6

5
请注意,在某些C++编译器中,您可以通过使用 #include <cstring> 而不是已弃用的 #include <string.h> 来避免编译器警告。 - Paul
1
你的例子只使用了ASCII字符集。您能否更新为Unicode字符集,特别是32位字符? - Thomas Weller

22

针对Unicode

这里有几个回答提到.length()在多字节字符时会给出错误的结果,但是有11个答案没有给出解决方案。

Z͉̳̺ͥͬ̾a̴͕̲̒̒͌̋ͪl̨͎̰̘͉̟ͤ̀̈̚͜g͕͔̤͖̟̒͝ͅo̵̡̡̼͚̐ͯ̅ͪ̆ͣ̚的情况

首先,重要的是要知道你所说的“长度”是什么意思。举个例子,考虑字符串“Z͉̳̺ͥͬ̾a̴͕̲̒̒͌̋ͪl̨͎̰̘͉̟ͤ̀̈̚͜g͕͔̤͖̟̒͝ͅo̵̡̡̼͚̐ͯ̅ͪ̆ͣ̚”(请注意,一些语言,尤其是泰语,实际上使用组合变音符号,因此这不仅仅适用于15岁的模因,但显然这是最重要的用例)。假设它是以UTF-8编码的。我们可以用3种方式来谈论这个字符串的长度:

95字节

00000000: 5acd a5cd accc becd 89cc b3cc ba61 cc92  Z............a..
00000010: cc92 cd8c cc8b cdaa ccb4 cd95 ccb2 6ccd  ..............l.
00000020: a4cc 80cc 9acc 88cd 9ccc a8cd 8ecc b0cc  ................
00000030: 98cd 89cc 9f67 cc92 cd9d cd85 cd95 cd94  .....g..........
00000040: cca4 cd96 cc9f 6fcc 90cd afcc 9acc 85cd  ......o.........
00000050: aacc 86cd a3cc a1cc b5cc a1cc bccd 9a    ...............

50个代码点

LATIN CAPITAL LETTER Z
COMBINING LEFT ANGLE BELOW
COMBINING DOUBLE LOW LINE
COMBINING INVERTED BRIDGE BELOW
COMBINING LATIN SMALL LETTER I
COMBINING LATIN SMALL LETTER R
COMBINING VERTICAL TILDE
LATIN SMALL LETTER A
COMBINING TILDE OVERLAY
COMBINING RIGHT ARROWHEAD BELOW
COMBINING LOW LINE
COMBINING TURNED COMMA ABOVE
COMBINING TURNED COMMA ABOVE
COMBINING ALMOST EQUAL TO ABOVE
COMBINING DOUBLE ACUTE ACCENT
COMBINING LATIN SMALL LETTER H
LATIN SMALL LETTER L
COMBINING OGONEK
COMBINING UPWARDS ARROW BELOW
COMBINING TILDE BELOW
COMBINING LEFT TACK BELOW
COMBINING LEFT ANGLE BELOW
COMBINING PLUS SIGN BELOW
COMBINING LATIN SMALL LETTER E
COMBINING GRAVE ACCENT
COMBINING DIAERESIS
COMBINING LEFT ANGLE ABOVE
COMBINING DOUBLE BREVE BELOW
LATIN SMALL LETTER G
COMBINING RIGHT ARROWHEAD BELOW
COMBINING LEFT ARROWHEAD BELOW
COMBINING DIAERESIS BELOW
COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW
COMBINING PLUS SIGN BELOW
COMBINING TURNED COMMA ABOVE
COMBINING DOUBLE BREVE
COMBINING GREEK YPOGEGRAMMENI
LATIN SMALL LETTER O
COMBINING SHORT STROKE OVERLAY
COMBINING PALATALIZED HOOK BELOW
COMBINING PALATALIZED HOOK BELOW
COMBINING SEAGULL BELOW
COMBINING DOUBLE RING BELOW
COMBINING CANDRABINDU
COMBINING LATIN SMALL LETTER X
COMBINING OVERLINE
COMBINING LATIN SMALL LETTER H
COMBINING BREVE
COMBINING LATIN SMALL LETTER A
COMBINING LEFT ANGLE ABOVE

5个字素

Z with some s**t
a with some s**t
l with some s**t
g with some s**t
o with some s**t

使用ICU查找长度

虽然ICU有C++类,但是它们需要转换为UTF-16。您可以直接使用C类型和宏来获得一些UTF-8支持:

#include <memory>
#include <iostream>
#include <unicode/utypes.h>
#include <unicode/ubrk.h>
#include <unicode/utext.h>

//
// C++ helpers so we can use RAII
//
// Note that ICU internally provides some C++ wrappers (such as BreakIterator), however these only seem to work
// for UTF-16 strings, and require transforming UTF-8 to UTF-16 before use.
// If you already have UTF-16 strings or can take the performance hit, you should probably use those instead of
// the C functions. See: http://icu-project.org/apiref/icu4c/
//
struct UTextDeleter { void operator()(UText* ptr) { utext_close(ptr); } };
struct UBreakIteratorDeleter { void operator()(UBreakIterator* ptr) { ubrk_close(ptr); } };
using PUText = std::unique_ptr<UText, UTextDeleter>;
using PUBreakIterator = std::unique_ptr<UBreakIterator, UBreakIteratorDeleter>;

void checkStatus(const UErrorCode status)
{
    if(U_FAILURE(status))
    {
        throw std::runtime_error(u_errorName(status));
    }
}

size_t countGraphemes(UText* text)
{
    // source for most of this: http://userguide.icu-project.org/strings/utext
    UErrorCode status = U_ZERO_ERROR;
    PUBreakIterator it(ubrk_open(UBRK_CHARACTER, "en_us", nullptr, 0, &status));
    checkStatus(status);
    ubrk_setUText(it.get(), text, &status);
    checkStatus(status);
    size_t charCount = 0;
    while(ubrk_next(it.get()) != UBRK_DONE)
    {
        ++charCount;
    }
    return charCount;
}

size_t countCodepoints(UText* text)
{
    size_t codepointCount = 0;
    while(UTEXT_NEXT32(text) != U_SENTINEL)
    {
        ++codepointCount;
    }
    // reset the index so we can use the structure again
    UTEXT_SETNATIVEINDEX(text, 0);
    return codepointCount;
}

void printStringInfo(const std::string& utf8)
{
    UErrorCode status = U_ZERO_ERROR;
    PUText text(utext_openUTF8(nullptr, utf8.data(), utf8.length(), &status));
    checkStatus(status);

    std::cout << "UTF-8 string (might look wrong if your console locale is different): " << utf8 << std::endl;
    std::cout << "Length (UTF-8 bytes): " << utf8.length() << std::endl;
    std::cout << "Length (UTF-8 codepoints): " << countCodepoints(text.get()) << std::endl;
    std::cout << "Length (graphemes): " << countGraphemes(text.get()) << std::endl;
    std::cout << std::endl;
}

void main(int argc, char** argv)
{
    printStringInfo(u8"Hello, world!");
    printStringInfo(u8"หวัดดีชาวโลก");
    printStringInfo(u8"\xF0\x9F\x90\xBF");
    printStringInfo(u8"Z͉̳̺ͥͬ̾a̴͕̲̒̒͌̋ͪl̨͎̰̘͉̟ͤ̀̈̚͜g͕͔̤͖̟̒͝ͅo̵̡̡̼͚̐ͯ̅ͪ̆ͣ̚");
}

这将打印:

UTF-8 string (might look wrong if your console locale is different): Hello, world!
Length (UTF-8 bytes): 13
Length (UTF-8 codepoints): 13
Length (graphemes): 13

UTF-8 string (might look wrong if your console locale is different): หวัดดีชาวโลก
Length (UTF-8 bytes): 36
Length (UTF-8 codepoints): 12
Length (graphemes): 10

UTF-8 string (might look wrong if your console locale is different): 
Length (UTF-8 bytes): 4
Length (UTF-8 codepoints): 1
Length (graphemes): 1

UTF-8 string (might look wrong if your console locale is different): Z͉̳̺ͥͬ̾a̴͕̲̒̒͌̋ͪl̨͎̰̘͉̟ͤ̀̈̚͜g͕͔̤͖̟̒͝ͅo̵̡̡̼͚̐ͯ̅ͪ̆ͣ̚
Length (UTF-8 bytes): 95
Length (UTF-8 codepoints): 50
Length (graphemes): 5

Boost.Locale包装了ICU,可能提供更好的接口。但是,它仍需要进行UTF-16的转换。


这是正确而有帮助的答案。 谢谢~ - Ali Tran
这是正确而有帮助的答案。 谢谢~ - undefined

18
这取决于你所说的字符串类型。有许多类型的字符串:
  1. const char* - C风格的多字节字符串
  2. const wchar_t* - C风格的宽字符串
  3. std::string - “标准”多字节字符串
  4. std::wstring - “标准”宽字符串
对于第3和第4种类型,您可以使用.size().length()方法。
对于第1种类型,您可以使用strlen(),但必须确保字符串变量不为NULL(=== 0)。
对于第2种类型,您可以使用wcslen(),但必须确保字符串变量不为NULL(=== 0)。
在非标准C ++库中还有其他字符串类型,例如MFC的CString,ATL的CComBSTR,ACE的ACE_CString等,具有诸如.GetLength()等方法。我不能立即记住它们所有的细节。

STLSoft库使用所谓的字符串访问shim将所有这些抽象出来,可以用于从任何类型获取字符串长度(和其他方面)。因此,对于上述所有内容(包括非标准库),都可以使用相同的函数stlsoft::c_str_len()本文描述了它是如何工作的,因为它并不完全明显或容易。


1
哪一个适用于32位字符,例如? - Thomas Weller

4
在C++的std::string中,length()和size()方法会给出字节数,而不一定是字符数!同样适用于c-Style的sizeof()函数!
对于大多数可打印的7bit-ASCII字符,这个值是相同的,但对于不是7bit-ASCII字符的字符,它绝对不是相同的。请看下面的例子以获得真实结果(64位linux)。
没有一个简单的c/c++函数可以真正计算字符的数量。顺便说一句,所有这些东西都是与实现相关的,可能在其他环境下(编译器、win 16/32、linux、嵌入式等)有所不同。
请看下面的例子:
#include <string>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

int main()
{
/* c-Style char Array */
const char * Test1 = "1234";
const char * Test2 = "ÄÖÜ€";
const char * Test3 = "αβγ";

/* c++ string object */
string sTest1 = "1234";
string sTest2 = "ÄÖÜ€";
string sTest3 = "αβγ";

printf("\r\nC Style Resluts:\r\n");
printf("Test1: %s, strlen(): %d\r\n",Test1, (int) strlen(Test1));
printf("Test2: %s, strlen(): %d\r\n",Test2, (int) strlen(Test2));
printf("Test3: %s, strlen(): %d\r\n",Test3, (int) strlen(Test3));

printf("\r\nC++ Style Resluts:\r\n");
cout << "Test1: " << sTest1 << ", Test1.size():  " <<sTest1.size() <<"  sTest1.length(): " << sTest1.length() << endl;
cout << "Test1: " << sTest2 << ", Test2.size():  " <<sTest2.size() <<"  sTest1.length(): " << sTest2.length() << endl;
cout << "Test1: " << sTest3 << ", Test3.size(): " <<sTest3.size() << "  sTest1.length(): " << sTest3.length() << endl;
return 0;
}
下面是示例的输出结果:
C Style Results:
Test1: ABCD, strlen(): 4    
Test2: ÄÖÜ€, strlen(): 9
Test3: αβγ, strlen(): 10

C++ Style Results:
Test1: ABCD, sTest1.size():  4  sTest1.length(): 4
Test2: ÄÖÜ€, sTest2.size():  9  sTest2.length(): 9
Test3: αβγ, sTest3.size(): 10  sTest3.length(): 10

2
如果您正在使用std::string,有两种常见的方法可以实现这一点:
std::string Str("Some String");
size_t Size = 0;
Size = Str.size();
Size = Str.length();

如果您正在使用C风格的字符串(使用char *或const char *),那么您可以使用以下代码:

const char *pStr = "Some String";
size_t Size = strlen(pStr);

2

如果你正在使用旧的C风格字符串而不是较新的STL风格字符串,那么C运行时库中有一个strlen函数:

const char* p = "Hello";
size_t n = strlen(p);

1
string foo;
... foo.length() ...

.length和.size是同义词,我只是认为"length"这个词更清晰明了。


不建议使用size(),因为我记得在某些实现中它会返回“length() + 1”(因为它包括空终止符)。所以最好使用length(),因为它更简洁和可移植。 - user19302
2
@LaQuet,如果你没有使用符合标准的编译器或STL实现,那么你需要担心更多的事情。 - strager
2
@Nelson,这不应该是一种情况,因为按照标准的 C++ 字符串并不以 null 结尾。但是当使用 c_str() 进行强制转换时,它会在末尾添加 null 字节。 - John T

1
std::string str("a string");
std::cout << str.size() << std::endl;

1

对于一个实际的字符串对象:

yourstring.length();

或者

yourstring.size();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接