你如何构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真正的幸福。