如果我想构造一个std :: string,其行如:

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

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

有帮助吗?

解决方案

从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之前

问题是const char*构造函数采用\0假设输入是C字符串。 C字符串c_str()终止,因此解析在到达vector<char>字符时停止。

为了弥补这一点,您需要使用从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 ++ <=> <=> - 已终止(如其他帖子所示)。但是,您可以使用方法<=>提取指向包含C-String的内部缓冲区的指针。

另请参阅 Doug T的回答下面关于使用<=>。

另请查看 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-strings

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

然而,当然,您遇到与c字符串相同的问题。您可能会忘记空终端或写入已分配的空间。

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

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

用户定义的文字为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"));

以下内容将有效......

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

你必须小心这一点。如果用任何数字字符替换'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

最好使用std :: vector <!> lt; char <!> gt;如果这个问题不只是出于教育目的。

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)

显然,你应该只在项目中使用一个或另一个解决方案,并按照你认为合适的方式调用它。

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top