98

如果我想构造一个带有如下行的 std::string :

std::string my_string("a\0b");

我想在结果字符串中包含三个字符(a,null,b),我只得到一个。什么是正确的语法?

4

11 回答 11

140

自 C++14 起

我们已经能够创建文字std::string

#include <iostream>
#include <string>

int main()
{
    using namespace std::string_literals;

    std::string s = "pl-\0-op"s;    // <- Notice the "s" at the end
                                    // This is a std::string literal not
                                    // a C-String literal.
    std::cout << s << "\n";
}

C++14 之前

问题是std::string采用 a 的构造函数const char*假设输入是 C 字符串。C-strings 被\0终止,因此当它到达\0字符时解析停止。

为了弥补这一点,您需要使用从 char 数组(而不是 C 字符串)构建字符串的构造函数。这需要两个参数 - 一个指向数组的指针和一个长度:

std::string   x("pq\0rs");   // Two characters because input assumed to be C-String
std::string   x("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.

注意:C++std::string没有终止 \0(如其他帖子中所建议的那样)。但是,您可以使用 方法提取指向包含 C-String 的内部缓冲区的指针c_str()

另请查看Doug T下面关于使用vector<char>.

另请查看RiaD以获得 C++14 解决方案。

于 2008-10-02T19:56:53.250 回答
22

如果您正在像使用 c 样式字符串(字符数组)一样进行操作,请考虑使用

std::vector<char>

您可以更自由地以与处理 c 字符串相同的方式将其视为数组。您可以使用 copy() 复制到字符串中:

std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());

你可以在许多可以使用 c-strings 的地方使用它

printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';

但是,很自然地,您会遇到与 c-strings 相同的问题。您可能会忘记您的空终端或写过去分配的空间。

于 2008-10-02T19:54:04.770 回答
13

我不知道你为什么想做这样的事情,但试试这个:

std::string my_string("a\0b", 3);
于 2008-10-02T19:42:53.223 回答
13

用户定义的文字为 C++ 添加了哪些新功能?给出了一个优雅的答案:定义

std::string operator "" _s(const char* str, size_t n) 
{ 
    return std::string(str, n); 
}

那么你可以这样创建你的字符串:

std::string my_string("a\0b"_s);

甚至这样:

auto my_string = "a\0b"_s;

有一种“旧式”的方式:

#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string

那么你可以定义

std::string my_string(S("a\0b"));
于 2012-10-05T01:36:19.380 回答
8

以下将工作...

std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
于 2008-10-02T19:48:20.307 回答
5

你必须小心这个。如果您将“b”替换为任何数字字符,您将使用大多数方法默默地创建错误的字符串。请参阅:C++ 字符串文字转义字符的规则

例如,我把这个看起来很无辜的片段放在程序中间

// Create '\0' followed by '0' 40 times ;)
std::string str("\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", 80);
std::cerr << "Entering loop.\n";
for (char & c : str) {
    std::cerr << c;
    // 'Q' is way cooler than '\0' or '0'
    c = 'Q';
}
std::cerr << "\n";
for (char & c : str) {
    std::cerr << c;
}
std::cerr << "\n";

这是该程序为我输出的内容:

Entering loop.
Entering loop.

vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

那是我的第一个打印语句两次,几个非打印字符,后跟一个换行符,然后是内部存储器中的某些内容,我刚刚覆盖了它(然后打印,表明它已被覆盖)。最糟糕的是,即使使用彻底和详细的 gcc 警告来编译它,我也没有任何迹象表明有什么问题,并且通过 valgrind 运行程序并没有抱怨任何不正确的内存访问模式。换句话说,现代工具完全无法检测到它。

你可以用更简单的方法来解决同样的问题std::string("0", 100);,但是上面的例子有点棘手,因此更难看出哪里出了问题。

幸运的是,C++11 使用初始化列表语法为我们提供了一个很好的解决方案。这使您不必指定字符数(如上所示,您可能会做错),并避免组合转义数字。std::string str({'a', '\0', 'b'})对于任何字符串内容都是安全的,这与采用数组char和大小的版本不同。

于 2012-10-14T17:00:53.160 回答
4

在 C++14 中,您现在可以使用文字

using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
于 2016-01-11T14:13:22.883 回答
1

如果这个问题不仅仅是为了教育目的,最好使用 std::vector<char> 。

于 2008-10-02T19:56:01.677 回答
1

anonym 的回答非常好,但在 C++98 中也有一个非宏解决方案:

template <size_t N>
std::string RawString(const char (&ch)[N])
{
  return std::string(ch, N-1);  // Again, exclude trailing `null`
}

使用此函数,RawString(/* literal */)将生成与以下相同的字符串S(/* literal */)

std::string my_string_t(RawString("a\0b"));
std::string my_string_m(S("a\0b"));
std::cout << "Using template: " << my_string_t << std::endl;
std::cout << "Using macro: " << my_string_m << std::endl;

此外,宏还有一个问题:表达式实际上不是std::string写的,因此不能用于例如简单的赋值初始化:

std::string s = S("a\0b"); // ERROR!

...所以最好使用:

#define std::string(s, sizeof s - 1)

显然,您应该只在项目中使用一种或另一种解决方案,并将其命名为您认为合适的任何名称。

于 2016-11-09T18:53:47.293 回答
-5

我知道这个问题已经问了很长时间了。但是对于任何遇到类似问题的人可能会对以下代码感兴趣。

CComBSTR(20,"mystring1\0mystring2\0")
于 2010-02-01T10:16:41.117 回答
-8

几乎所有 std::strings 的实现都是空终止的,所以你可能不应该这样做。请注意,“a\0b”实际上是四个字符长,因为自动空终止符(a、null、b、null)。如果你真的想这样做并打破 std::string 的合同,你可以这样做:

std::string s("aab");
s.at(1) = '\0';

但如果你这样做了,你所有的朋友都会嘲笑你,你永远不会找到真正的幸福。

于 2008-10-02T19:47:12.760 回答