【问题标题】:Is there a more efficient way to set a std::vector from a stream?有没有更有效的方法从流中设置 std::vector ?
【发布时间】:2012-06-05 02:24:14
【问题描述】:

目前,我从std::ostringstream 中设置std::vector<char> 的值如下:

void
foo(std::vector<char> &data, std::stringstream &stream) {
  data = std::vector<char>(stream.str().begin(), stream.str().end());
}

我想知道是否有更有效的方法可以在 C++ 中使用 STL 执行此操作,或者我在此处给出的方法是否被认为合适?改用std::stringstream 会更好吗?

【问题讨论】:

  • 我不确定这样做的效率如何,但它不正确的。对.str() 的两次调用返回不同的对象。
  • 感谢您指出该错误,我以为我得到的是参考而不是 str() 的副本。

标签: c++ stl stdvector stringstream ostringstream


【解决方案1】:

如果有更有效的方法

您可能希望在 data 上调用 reserve 并直接在 data 上使用范围 insert 成员,而不是使用复制分配。您需要记住的关于vectors 的事情是每个节点都可能会增加大小(并重新定位所有元素)。因此,您最好一次性分配内存(如果您知道要存储多少对象 - 您在此处确实知道)并利用这一事实。

【讨论】:

  • 我实际上会假设大多数实现将在基于迭代器的构造函数中使用std::distance 调用来自己执行该逻辑。
  • @KillianDS :可能只适用于随机访问迭代器,否则范围将不必要地遍历两次。
  • @ildjarm: 不一定适用于输入迭代器,它不能被遍历两次。
【解决方案2】:

正如 cmets 中所指出的,由于两次调用 str(),您的代码不正确。为了提高效率,你可以避免创建一个临时的vector,像这样:

void foo(std::vector<char> &data, std::stringstream &stream) {
    const std::string& str = stream.str();
    data.assign( str.begin(), str.end() );
}

您还可以通过使用std::istreambuf_iterators 来避免std::string

void foo(std::vector<char> &data, std::stringstream &stream) {
    data.assign(
        std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>()
    );
}

但鉴于这些是输入迭代器vector 没有机会知道将分配多少数据并且性能可能会更差,因为它无法reserve 有足够的空间来避免重新分配。

【讨论】:

  • 我想你可能会想要istreambuf_iterator而不是istream_iterator,否则你会丢失空格。
  • 是的,除了不指定字符类型。 :-]
  • @ildjarn:需要吗?我正在检查的参考资料说它默认为char。确实,是需要的。在该特定参考中似乎是一个怪癖。
  • §24.6.3 说签名是template&lt;class charT, class traits = char_traits&lt;charT&gt; &gt; class istreambuf_iterator,没有默认值。
  • 哦顺便说一句,istreambuf_iterators 可以从流中构造,不需要调用rdbuf。 :)
【解决方案3】:

您的方法调用未定义的行为stream.str() 返回一个字符串按值,也就是一个临时字符串。您使用一个临时的 begin 迭代器和另一个的 end 迭代器,创建了一个无效的范围。

将流转换为容器的一种方法是使用通用迭代器接口:

#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>

int main(){
  std::stringstream src("....");
  std::vector<char> dest;
  // for a bit of efficiency
  std::streampos beg = src.tellg();
  src.seekg(0, std::ios_base::end);
  std::streampos end = src.tellg();
  src.seekg(0, std::ios_base::beg);
  dest.reserve(end - beg);

  dest.assign(std::istreambuf_iterator<char>(src), std::istreambuf_iterator<char>());

  std::copy(dest.begin(), dest.end(), std::ostream_iterator<char>(std::cout));
}

Live example on Ideone.

另一种方法是缓存返回的std::string 对象:

std::string const& s = stream.str();
data.reserve(s.size());
data.assign(s.begin(), s.end());

【讨论】:

  • 为什么会有两个 std::copy() 调用?它们对我来说似乎是多余的。
  • @WilliamKF:第一个(现在被.assign替换)是插入向量,第二个输出到std::cout(对于手写循环来说太懒了)。
  • 我想希望src.rdbuf()-&gt;in_avail() 给出正确的尺寸而不弄乱seek 是不是太过分了?我不擅长流库。
  • @Steve:看来你比我强,因为我什至不记得那个功能。不过,不知道它是否会起作用。
【解决方案4】:

从流迭代器复制到后插入迭代器:

std::istream src;
std::vector<char> dst;

std::copy(std::istream_iterator<char>(src), std::istream_iterator<char>(), std::back_inserter(dst));

istream_iterator 使用格式化转换(即跳过空格),因此这可能不是您想要的。我不确定你的目标是什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-31
    • 2018-10-15
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多