从 C++ 中的 std::string 获取字节

Posted

技术标签:

【中文标题】从 C++ 中的 std::string 获取字节【英文标题】:Get bytes from std::string in C++ 【发布时间】:2009-02-02 21:28:58 【问题描述】:

我在一个 C++ 非托管项目中工作。

我需要知道如何获取像“一些要加密的数据”这样的字符串并获得一个字节[] 数组,我将使用它作为 Encrypt 的源。

在 C# 中我会这样做

  for (int i = 0; i < text.Length; i++)
    buffer[i] = (byte)text[i];

我需要知道的是如何使用非托管 C++ 做同样的事情。

谢谢!

【问题讨论】:

【参考方案1】:

如果你只需要只读权限,那么c_str() 就可以了:

char const *c = myString.c_str();

如果您需要读/写访问权限,则可以将字符串复制到向量中。向量为您管理动态内存。那么你不必搞乱分配/解除分配:

std::vector<char> bytes(myString.begin(), myString.end());
bytes.push_back('\0');
char *c = &bytes[0];

【讨论】:

如果他想要一个字节数组,他需要终止'\0'吗?在这种情况下,您可以将 data() 用于只读。 我不确定他是否需要 \0。如果他不这样做,他现在知道他可以使用 .data() 。感谢您对此发表评论,马丁。 这个答案似乎正确并且可能有效,但假设 char 向量的数据存储是连续的并且不会改变是危险的。 假设它是连续的一点也不危险,它是向量的保证(C++ 规范 IIRC 中 std::vector 下的第一段)。假设它不会改变是安全的,前提是某些指定的函数没有被调用——相当于那些可能引发 resize() 的函数。 马克,“假设”这一点是非常安全的,因为向量总是连续的 :) 并且假设指针是有效的也是安全的。您不会修改向量,因此它必须重新分配其缓冲区。所以向量的第一个元素地址当然保持不变。【参考方案2】:

std::string::data 似乎是足够且最有效的。如果您想使用非常量内存来操作(加密很奇怪),您可以使用 memcpy 将数据复制到缓冲区:

unsigned char buffer[mystring.length()];
memcpy(buffer, mystring.data(), mystring.length());

STL 粉丝会鼓励您改用 std::copy:

std::copy(mystring.begin(), mystring.end(), buffer);

但这确实没有太大的好处。如果您需要空终止,请使用std::string::c_str() 和其他人提供的各种字符串复制技术,但我通常会避免这种情况,只需查询length。特别是对于密码学,您只知道有人会尝试通过将空值插入其中来尝试破解它,并且使用 std::string::data() 会阻止您懒惰地对字符串中的底层位进行假设。

【讨论】:

您能否说明使用 string::data() 比使用 string::begin() 更好?一个返回指向底层存储的指针,另一个返回一个迭代器,这对安全性有何帮助? 可变长度数组,如 unsigned char buffer[mystring.length()] 不是标准 C++。一些编译器支持它们,但 Visual Studio 不支持。【参考方案3】:

通常,加密函数需要

encrypt(const void *ptr, size_t bufferSize);

作为参数。可以直接传递 c_str 和 length:

encrypt(strng.c_str(), strng.length());

这样,额外的空间被分配或浪费。

【讨论】:

如果你传递一个指针和一个长度,那么你应该使用 data() 而不是 c_str() 来表明它没有被用作字符串。【参考方案4】:

如果您想获取 char_t 缓冲区指针,可以从 std::string 使用 c_ptr() 方法。

看起来你只是想将字符串的字符复制到一个新的缓冲区中。我会简单地使用std::string::copy 函数:

length = str.copy( buffer, str.size() );

【讨论】:

某些 std::string 的实现可能会使用引用计数,因此副本不一定会产生可以安全写入的新字节。【参考方案5】:

如果你只是需要读取数据。

encrypt(str.data(),str.size());

如果您需要数据的读/写副本,请将其放入向量中。 (不要动态分配空间,这是向量的工作)。

std::vector<byte>  source(str.begin(),str.end());
encrypt(&source[0],source.size());

当然我们都假设字节是一个字符!!!

【讨论】:

【参考方案6】:

C++17 及以后你可以使用std::byte 来表示实际的字节数据。我会推荐这样的东西:

std::vector<std::byte> to_bytes(std::string const& s)

    std::vector<std::byte> bytes;
    bytes.reserve(std::size(s));
      
    std::transform(std::begin(s), std::end(s), std::back_inserter(bytes), [](char c)
        return std::byte(c);
    );

    return bytes;

【讨论】:

其实 std::byte 并没有出现在 C++11 中,而是出现在 C++17 中。见en.cppreference.com/w/cpp/types/byte @Nikita128 非常正确,感谢您指出这一点。现已修复!【参考方案7】:

如果这只是普通的 C,那么:

strcpy(buffer, text.c_str());

假设缓冲区已分配并且足够大以容纳“文本”的内容,这是您原始代码中的假设。

如果 encrypt() 采用 'const char *' 那么你可以使用

encrypt(text.c_str())

而且你不需要复制字符串。

【讨论】:

【参考方案8】:

你可以使用range-based for 循环,看起来像这样:

std::vector<std::byte> getByteArray(const string& str)

    std::vector<std::byte> buffer;
    for (char str_char : str)
        buffer.push_back(std::byte(str_char));

    return buffer;

【讨论】:

【参考方案9】:

我认为您不想使用那里的 c# 代码。他们提供 System.Text.Encoding.ASCII(也是 UTF-*)

string str = "some text;
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(str);

您的问题源于忽略 c# 中的编码而不是您的 c++ 代码

【讨论】:

以上是关于从 C++ 中的 std::string 获取字节的主要内容,如果未能解决你的问题,请参考以下文章

std::string 与字节缓冲区(c++ 中的差异)

C++ std::string::size()函数(返回字符串的长度,以字节为单位)(与std::string::length()函数相同)

为啥 C++ 中的 main() 没有重载以使用 std::string?

如何获取 std::string 中的字符数?

[C++][原创]std::string获取文件名后缀

C++ 字符串 - 如何避免获取无效指针?