一个 std::string 可以包含嵌入的空值吗?

49

对于普通的C字符串,空字符'\0'表示数据的结束。

那么对于std::string类型的字符串,我能否包含空字符呢?


4个回答

53

是的,您可以在 std::string 中嵌入空值。

例如:

std::string s;
s.push_back('\0');
s.push_back('a');
assert(s.length() == 2);

注意:std::stringc_str()成员函数总是将空字符附加到返回的char缓冲区中;然而,std::stringdata()成员函数可能会在返回的char缓冲区中附加空字符,也可能不会。

小心使用operator+=

需要注意的一点是不要在RHS上使用char*operator+=。它只会添加到空字符为止。

例如:

std::string s = "hello";
s += "\0world";
assert(s.length() == 5);

正确的方式:

std::string s = "hello";
s += std::string("\0world", 6);
assert(s.length() == 11);

存储二进制数据时通常使用 std::vector

一般来说,使用 std::vector 存储任意二进制数据更为常见。

std::vector<char> buf;
buf.resize(1024);
char *p = &buf.front();

因为std::stringdata()c_str() 成员函数返回 const 指针,所以内存不可修改,使用 &buf.front() 可以直接修改缓冲区的内容,这种方式更常见。


2
在C++9x中,&s.front()也是可修改的,并且保证指向一个连续的缓冲区。虽然在C++03中没有这样的保证,但是在实践中没有已知的C++实现不符合这个规定(这也是为什么它很快被添加到C++0x的部分原因)。 - Pavel Minaev
11
请注意,从C++11起,.c_str().data 是同义词。特别是,这意味着由.data返回的字符串必须附带一个空终止符。 - nneonneo
@PavelMinaev:我猜“C++9x”是“C++0x”的错别字(你发表评论后不久就成为了C++11)。 - Keith Thompson
s.append("\0world", 6); is better than s += std::string("\0world", 6); - n.caillou

7

是的。std::string只是一个带有优势的vector<char>

然而,当将这样的“野兽”传递给调用.c_str()并停在0处的函数时,请小心。


1
第一个不正确,正如我最近了解到的那样。向量的交换保留迭代器和内容的引用,而字符串则不一定如此。https://dev59.com/poLba4cB1Zd3GeqPckxs - Notinlist
@Notinlist:它还有一个不同的名称!哦,太可怕了。 - Lightness Races in Orbit

1
你可以这样做,但为什么要这样做呢?在std::string中嵌入NUL会带来麻烦,因为你传递给一个std::string的函数可能会使用它的c_str()成员,并且大多数函数都会假定第一个NUL表示字符串的结尾。因此,这不是一个好主意。另外请注意,在UTF-8中,只有'\0'会导致0,因此即使是为了国际化的目的,也没有理由嵌入NUL。

谢谢您解释为什么这样做。 - Snoop
3
不,这很荒谬。“不要使用std::string的全部功能,因为你可能会将c_str()的结果传递给C字符串函数而没有传递长度”,真的吗?好吧,如果你从来没有这样做过,那么你就没问题了... - Lightness Races in Orbit

-1

没错,这是有效的。

你可以在字符串的中间有一个空字符。

然而,如果你在std::string中使用一个带有空字符的c字符串函数,则会进入未定义的行为区域 - 没有人想去那里!

 int n = strlen( strWithNullInMiddle.c_str() ); // Boom!!!

20
strlen 函数会返回第一个空字符(\0)之前的字符数。这可能不是预期的行为,但它并非未定义行为。 - Matthew Flaschen

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