如果我想用以下行构造std :: string:
std::string my_string("a\0b");
我想在结果字符串中包含三个字符(a,null,b)时,我只能得到一个。正确的语法是什么?
Answers:
我们已经能够创建文字 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";
}
问题是std::string
构造函数const char*
假设输入是C字符串。C字符串\0
终止,因此在到达\0
字符时解析停止。
为了弥补这一点,您需要使用从char数组(而非C-String)构建字符串的构造函数。这需要两个参数-指向数组的指针和一个长度:
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解决方案。
如果您要像使用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弦相同的问题。您可能会忘记您的空终端或写超出分配的空间。
byte *bytes = new byte[dataSize]; std::memcpy(bytes, image.data, dataSize * sizeof(byte)); std::string test(reinterpret_cast<char *>(bytes)); std::cout << "Encoded String length " << test.length() << std::endl;
我不知道您为什么要这样做,但是请尝试以下操作:
std::string my_string("a\0b", 3);
vector<unsigned char>
或被unsigned char *
发明的。
std::string
指出数据应该被视为纯文本,但是我正在做一些散列工作,并且我想确保所涉及的所有空字符仍然有效。这似乎是对带有嵌入的空字符的字符串文字的有效使用。
\0
UTF-8字符串中的一个字节只能为NUL。多字节编码字符绝不会包含-也不包含\0
任何其他ASCII字符。
用户定义的文字为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"));
您必须对此小心。如果将“ 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
和大小的版本不同。
在C ++ 14中,您现在可以使用文字
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
如果此问题不仅是出于教育目的,最好使用std :: vector <char>。
匿名者的答案非常好,但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)
显然,您应该仅在项目中使用一种或另一种解决方案,并在您认为合适的地方调用它。
几乎所有std :: strings的实现都以null终止,因此您可能不应该这样做。请注意,由于自动空终止符(a,null,b,null),“ a \ 0b”实际上是四个字符长。如果您确实想这样做并破坏std :: string的合同,则可以执行以下操作:
std::string s("aab");
s.at(1) = '\0';
但是,如果您这样做,所有的朋友都会嘲笑您,您将永远找不到真正的幸福。