如何构造一个嵌入 null 的 std::string ?

问题描述 投票:0回答:12

如果我想用如下行构造一个 std::string :

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

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

c++ null stdstring
12个回答
158
投票

自 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
const char*
构造函数假设输入是 C 字符串。 C 字符串以
\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
NOT
\0
终止的(如其他帖子中所建议的)。但是,您可以使用方法
c_str()
提取指向包含 C 字符串的内部缓冲区的指针。

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

Doug T 的回答

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


23
投票

如果您正在像使用 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 字符串的地方使用它

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

但是,您自然会遇到与 C 字符串相同的问题。您可能会忘记您的空终端或写过分配的空间。


13
投票

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

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

13
投票

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

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"));

8
投票

以下内容将起作用...

std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');

6
投票

你必须小心这一点。如果将“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
数组和大小的版本不同。
    


6
投票

using namespace std::literals::string_literals; std::string s = "a\0b"s; std::cout << s.size(); // 3



1
投票


1
投票

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)

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


0
投票
sv

后缀从 std::string_view 进行构造:

using namespace std::literals; // or
using namespace std::literals::string_view_literals;

auto sv = "a\0b"sv;
auto s = std::string{sv};
std::cout << s.size(); // 3

如果稍后需要使用视图,可能会更有用,否则直接用 
""s

后缀构造字符串

    


-5
投票

CComBSTR(20,"mystring1\0mystring2\0")



-8
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.