【问题标题】:How to read N integers into a vector?如何将 N 个整数读入向量?
【发布时间】:2019-01-18 12:30:05
【问题描述】:

如果我想从标准输入读取所有个整数到向量,我可以使用方便:

vector<int> v{istream_iterator<int>(cin), istream_iterator()};

但假设我只想读取n 整数。手写循环是我得到的一切吗?

vector<int> v(n);
for(vector<int>::size_type i = 0; i < n; i++)
    cin >> v[i];

或者有没有更右手的方法来做到这一点?

【问题讨论】:

标签: c++ vector input


【解决方案1】:

您通常不应该对 std::copy_n 执行此操作,它假定提供的迭代器在递增 n 次时仍然有效:

count 值从first 开始的范围精确复制到result 开始的范围。形式上,对于每个非负整数i &lt; n,执行*(result + i) = *(first + i)

(cppreference.com article on std::copy_n)

如果你能保证,那很好,但通常使用std::cin 这是不可能的。你可以很容易地让它解除对无效迭代器的引用:

默认构造的std::istream_iterator 称为流结束迭代器。当一个有效的std::istream_iterator 到达底层流的末尾时,它就等于流的末尾迭代器。取消引用或增加它会进一步调用未定义的行为。

(cppreference.com article on std::istream_iterator)

你的循环已经差不多了,尽管我可能会使用更强的终止条件来避免从“死”流中进行过多读取:

vector<int> v(n);
for(vector<int>::size_type i = 0; i < n; i++)
    if (!cin >> v[i])
       break;

实际上我很想把它包装成类似std::copy_n 的东西,但接受一个完整的“范围”,除了从 0N 计数之外,它的边界还可以被验证.

实现可能如下所示:

template<class InputIt, class Size, class OutputIt>
OutputIt copy_atmost_n(InputIt first, InputIt last, Size count, OutputIt result)
{
   for (Size i = 0; i < count && first != last; ++i)
      *result++ = *first++;
   return result;
}

你会这样使用它:

copy_atmost_n(
   std::istream_iterator<int>(std::cin),
   std::istream_iterator<int>(),
   N,
   std::back_inserter(v)
);

现在您得到 M 个元素,其中 M 是提供的输入数或 N,以较小者为准。

(live demo)

【讨论】:

  • 为了确认我正确地解释了这个答案,copy_n 的问题是如果流在读取 n 个元素之前遇到问题,行为是未定义的?所以基本上,“如果你信任你的数据源,就去吧,但如果你不信任,就不要使用copy_n?”
  • @templatetypedef 差不多 - 如果你“去做”,你会在我想的每一步保存一个迭代器比较。但对于一般用途,我将使用 copy_atmost_n 从这里开始 tbh
  • @templatetypedef 请注意,我正在专门考虑 EOF - tbh 我不确定如果有数据但读取/解析失败会发生什么
【解决方案2】:

如 cmets 中所述,copy_n 对这项工作不安全,但您可以将 copy_if 与可变 lambda 一起使用:

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

int main(){
    const int N = 10;
    std::vector<int> v;
    //optionally v.reserve(N);
    std::copy_if(
        std::istream_iterator<int>(std::cin),
        std::istream_iterator<int>(), 
        std::back_inserter(v), 
        [count=N] (int)  mutable {
            return count && count--;
    });

    return 0;
}

正如在这个答案中指出的那样: std::copy n elements or to the end

【讨论】:

  • 输入不够会不会导致UB?
  • 你无法修复它。 std::copy_n 不适合此任务。
  • 还是没有。取消引用 std::istream_iterator “end iterator” 具有 UB,而不是 throw 语义。 (见我的回答)
  • @bartop 我建议删除 copy_n 部分并保留 如 cmets 中给出的那样,copy_n 是不安全的,但您可以将 copy_if 与可变 lambda 一起使用: 后跟您的 nice解决方案。
  • 带有copy_if 的版本更好(取消投票)。 技术上不能保证谓词会被“按顺序”调用,但我们保证它会被准确地调用 次,并且没有充分的实际理由叫它不正常。所以这应该有效。尽管如此,像我的 copy_atmost_n 这样的包装器更简洁、更具表现力并且更容易正确处理。
猜你喜欢
  • 2012-09-28
  • 2019-05-11
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 1970-01-01
相关资源
最近更新 更多