【问题标题】:Reading directly from an std::istream into an std::string直接从 std::istream 读取到 std::string
【发布时间】:2010-12-21 10:33:55
【问题描述】:

有没有办法直接将已知数量的字节读取到 std::string 中,而无需创建临时缓冲区?

例如,目前我可以通过

boost::uint16_t len;
is.read((char*)&len, 2);
char *tmpStr = new char[len];
is.read(tmpStr, len);
std::string str(tmpStr, len);
delete[] tmpStr;

【问题讨论】:

  • 您是否考虑过使用vector<char> 而不是string?如果您的数据比“类字符串”更“原始”,它可能更适合您,并且直接访问的混淆更少。 (向量需要连续存储,所以使用&v[0]。)
  • 大部分是字符串数据,只是嵌入在大型二进制文件中。此外,我只想更改加载例程,而不是在加载后使用数据的 1000 行代码,从 std::string 进行更改需要。
  • 然后我会检查你的具体字符串实现,然后使用 GMan 的答案,确保你也检查is.read 之后的流。
  • 我看到这是旧的,但我只需要对未来的读者发表评论:请不要相信流中的字节的长度值,因为它们可能被损坏或被恶意用户操纵。在这种情况下,64KB 可能并不算太糟糕,但你永远不知道。我认为一些 GIF 和 JPG 代码执行错误是由这样的代码造成的。
  • 当然。为字符串创建一个具有 4 字节长度字段的流。使用int。从流中读取 4 个字节,加 1 并为字符串加上空终止符分配那么多。如果邪恶流使用0xFFFF 作为长度怎么办?读作-1。你加 1 得到 0,malloc 0,然后事情就会从那里走下坡路,具体取决于细节。

标签: c++ stdstring iostream


【解决方案1】:

std::string 有一个你可以使用的 resize 函数,或者一个可以做同样事情的构造函数:

boost::uint16_t len;
is.read((char*)&len, 2);

std::string str(len, '\0');
is.read(&str[0], len);

这是未经测试的,我不知道是否要求字符串具有连续存储。

【讨论】:

  • 字符串被定义为向量。相同的连续性。
  • 它们没有被定义为向量,但 21.3.4/1 确实意味着连续存储。但是,关于该特定部分存在混淆和缺陷报告,我不确定当前的共识是什么,也不确定根据该解释的可移植性。
  • @Roger。我不同意 21.3.4/1 意味着连续存储。 c_str() 和 data() 的存在暗示了这一点,但这只是因为有效的实现需要连续的存储来实现它们。我相信下一个版本的标准也会消除这种情况。
  • 这是标准中的一个已知缺陷,将在 C++0x 中得到纠正。我不知道任何不使用连续存储的实现。不过,您总是可以输入一个断言来检查它是否是连续的。
  • 在 C++11, 21.4.1/5 中它说“基本字符串对象中的类字符对象应连续存储”。
【解决方案2】:

您可以使用 copy_n 和 insert_iterator 的组合

void test_1816319()
{
    static char const* fname = "test_1816319.bin";
    std::ofstream ofs(fname, std::ios::binary);
    ofs.write("\x2\x0", 2);
    ofs.write("ab", 2);
    ofs.close();

    std::ifstream ifs(fname, std::ios::binary);
    std::string s;
    size_t n = 0;
    ifs.read((char*)&n, 2);
    std::istream_iterator<char> isi(ifs), isiend;
    std::copy_n(isi, n, std::insert_iterator<std::string>(s, s.begin()));
    ifs.close();
    _unlink(fname);

    std::cout << s << std::endl;
}

没有复制,没有黑客,没有溢出的可能性,没有未定义的行为。

【讨论】:

  • 如果您正在做我认为您正在做的事情,那么请阅读此link 以及随附的代码。
  • 这里不是这样,但是如果文件结束或遇到错误,copy_n 是否安全?
  • 我使用您的方法构建了一些代码:code review。谢谢!
【解决方案3】:

你可以使用 getline 之类的东西:

#include <iostream>
#include <string>
using namespace std;

int main () {
  string str;
  getline (cin,str,' ');
}

【讨论】:

  • 这是对其他问题的一个很好的建议,但不适用于这个问题:特定字节数的未格式化输入。
  • 这没有回答问题,因为它没有读取指定数量的字节。即使这样做了,getline 也必须查看它为分隔符读取的每个字节,这在指定字节数时是昂贵且不必要的。这个答案应该被删除。
【解决方案4】:

我会使用向量作为缓冲区。

boost::uint16_t len;
is.read((char*)&len, 2); // Note if this file was saved from a different architecture 
                         // then endianness of these two bytes may be reversed.

std::vector buffer(len);  // uninitialized.
is.read(&buffer[0], len);

std::string  str(buffer.begin(),buffer.end());

虽然您可能会使用字符串作为缓冲区(如 GMan 所述)。标准不保证字符串成员位于连续的位置(因此请检查您当前的实现并在移植到另一个编译器/平台时添加需要检查的重要注释)。

【讨论】:

  • " 标准不保证字符串成员位于连续位置"
  • @xaxxon:是的。但是上面的代码不需要字符串来将元素存储在连续的位置。现在,如果您指的是向量(并且只是偶然提到了字符串),则此代码确实做出了该假设。但正如您从 C++11 开始所指出的,这已得到保证。同样在 2011 年更新 C++ 标准之前,对所有主要实现进行了调查(大约在 2007 年),所有主要实现都将向量实现为连续块(这使得更新标准变得容易)。
【解决方案5】:

您是在优化代码长度还是尝试在此处为自己保存一份副本?临时缓冲区有什么问题?

我认为你实际上是在规避字符串的保护,试图直接写那样做。如果您担心复制到 std::string 的性能,因为您已经确定它会以某种方式影响应用程序的性能,我会直接使用 char*。

编辑:做更多的寻找...... initializing std::string from char* without copy

在第二个答案中,非常明确地表示您无法实现您想要实现的目标(即填充 std::string 而无需对 char* 进行迭代以进行复制。)

查看您的加载例程(也许在这里发布?)并最小化分配:new 和 delete 当然不是免费的,因此如果您不必不断地重新创建缓冲区,您至少可以节省一些时间.我总是发现通过 memset 将缓冲区设置为 0 或 null 以在每次迭代时终止数组的第一个索引来擦除它很有帮助,但是一旦你对自己的算法有信心,你可以迅速删除该代码以提高性能。

【讨论】:

  • std::string 的性能很好,问题是从二进制文件将数据加载到它们中,目前这需要很长的时间。分析表明,70% 的加载时间是读取字符串,只有 30% 是其他二进制数据或少量处理,因此加快字符串读取似乎是大幅加快整个过程的明显解决方案。所以我绝不想在程序的其余部分中替换 std::string ,这意味着更改 1000 行,而不仅仅是更改字符串加载例程。
  • 每次迭代的 char* 的 alloc、dealloc 问题有多大?如果您只是保留一个足够大小的 char*(显然是检查每次迭代)并从该单个 char* 创建新字符串怎么办?
【解决方案6】:

一个简单的方法是:

std::istream& data
const size_t dataSize(static_cast<size_t>(data.rdbuf()->in_avail()));
std::string content;
content.reserve( dataSize);
data.read(&content[0], dataSize);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 2013-01-19
    • 1970-01-01
    • 2021-02-24
    相关资源
    最近更新 更多