你如何构build一个embedded式null的std :: string?

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

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

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

自从C ++ 14

我们已经能够创build字面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*假设input是一个Cstring。 Cstring被\0终止,因此parsing到达\0字符时停止。

为了弥补这一点,您需要使用从char数组(不是Cstring)构buildstring的构造函数。 这需要两个参数 – 一个指向数组的指针和一个长度:

 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 不是终止的(如其他文章中的build议)。 但是,您可以使用方法c_str()提取指向包含C-String的内部缓冲区的指针。

还请看下面的Doug T的答案 ,关于使用一个vector<char>

另外检查一下RIAD的C ++ 14解决scheme。

如果你像使用c风格的string(字符数组)那样进行操作,请考虑使用

 std::vector<char> 

你有更多的自由来对待它像一个数组,就像对待一个Cstring一样。 你可以使用copy()复制到一个string中:

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

你可以在许多相同的地方使用它,你可以使用Cstring

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

然而,你自然会遇到和c-string一样的问题。 你可能忘记你的空terminal或写过去分配的空间。

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

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

用户定义的文字添加到C ++中的新function是什么? 呈现一个优雅的答案:定义

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

那么你可以这样创build你的string:

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

以下将工作…

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

你必须小心这个。 如果用任何数字字符replace“b”,则会使用大多数方法静静地创build错误的string。 请参阅: C ++string文字转义字符的规则 。

例如,我把这个无辜的片段放在一个程序的中间

 // 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 

这是我的第一次打印声明,两次,几个非打印字符,后面跟着一个换行符,后面是内存中的内容,我刚刚覆盖(然后打印,显示它已被覆盖)。 最糟糕的是,甚至用彻底和详细的海湾合作委员会警告来编译这个报告,也没有发现任何错误,通过valgrind运行这个程序并没有抱怨任何不正确的内存访问模式。 换句话说,现代工具是完全无法察觉的。

你可以用简单得多的std::string("0", 100);来得到这个问题std::string("0", 100); ,但是上面的例子有点棘手,因此很难看到有什么问题。

幸运的是,C ++ 11使用初始化器列表语法为我们提供了一个很好的解决scheme。 这样可以避免必须指定字符数(正如我上面所显示的那样,您可以做的不正确),并避免将转义数字组合在一起。 std::string str({'a', '\0', 'b'})对于任何string内容都是安全的,与使用char和size的数组不同。

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

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

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

anonym的答案非常好,但是C ++ 98中还有一个非macros的解决scheme:

 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 */)相同的string:

 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; 

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

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

…所以最好使用:

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

显然,你应该只使用你的项目中的一个或其他解决scheme,并把它称之为适当的。

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

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

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

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

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