使用C++ ifstream从文本文件中读取整数

20

我想要从文本文件中读取图的邻接信息并将其存储到一个向量中。

  • 该文件有任意数量的行

  • 每一行都以'\n'结尾,包含任意数量的整数

例如:

First line:
0 1 4
Second line:
1 0 4 3 2
Thrid line:
2 1 3
Fourth line:
3 1 2 4
Fifth line:
4 0 1 3
如果我使用getline()逐行读取文本,如何解析每一行(因为每一行可能包含不同数量的整数)?有什么建议吗?

您可以参考以下链接:https://dev59.com/k3VC5IYBdhLWcg3wnCj6 - Ryan Li
2个回答

20
标准的行读取习惯用法:
#include <fstream>
#include <sstream>
#include <string>
#include <vector>


std::ifstream infile("thefile.txt");
std::string line;

while (std::getline(infile, line))
{
  std::istringstream iss(line);
  int n;
  std::vector<int> v;

  while (iss >> n)
  {
    v.push_back(n);
  }

  // do something useful with v
}

这是使用for循环的一行代码版本。我们需要一个辅助构造函数(感谢@Luc Danton!),它与std::move相反:

namespace std
{
  template <typename T> T & stay(T && t) { return t; }
}

int main()
{
  std::vector<std::vector<int>> vv;

  for (std::string line;
       std::getline(std::cin, line);
       vv.push_back(std::vector<int>(std::istream_iterator<int>(std::stay(std::istringstream(line))),
                                     std::istream_iterator<int>())
                    )
       ) { }

  std::cout << vv << std::endl;
}

@Nawaz:我的早期版本涉及到一个更加复杂的结构体和一个const-cast,这是C++98。如果你感兴趣,请私下与我联系,这不适合在公共场合讨论 :-) - Kerrek SB
@Kerrek,感谢您的解决方案。当我有嵌套的STL容器,比如vector<vector<int>>时,我该如何遍历它?我尝试了以下代码:std::vector<std::vector<int>> iter1; std::vector<int>::iterator iter2; for (iter1 = vv.begin(); iter1 != vv.end(); ++iter1) { for (iter2 = (*vv).begin(); iter2 != (*vv).end(); ++iter2) cout << *iter2 << " "; cout << endl; } 但是它无法编译。遍历嵌套的STL容器的正确方法是什么?谢谢。 - itnovice
@itnovice: 大致就是这个样子。for (std::vector<std::vector<int>>::const_iterator it1 = v.begin(), end1 = v.end(); it1 != end1; ++it1) { for ( std::vector<int>::const_iterator it2 = it1->begin(), end2 = it1->end(); it2 != end2; ++it2) { ... } } 等等。 - Kerrek SB
@itnovice:太好了,我很高兴。使用第一个版本吧。第二个版本纯粹是为了“如果不能用一行代码完成,那我就算倒霉了。” - Kerrek SB
6
std::stay”从什么时候开始允许向std中添加名称?(注意:这是对原文的翻译,没有解释或其他内容的返回) - curiousguy
显示剩余2条评论

9

首先使用std::getline函数读取一行,然后使用std::stringstream从该行中读取整数,如下:

std::ifstream file("input.txt");

std::vector<std::vector<int>> vv;
std::string line;
while(std::getline(file, line))
{
    std::stringstream ss(line);
    int i;
    std::vector<int> v;
    while( ss >> i ) 
       v.push_back(i);
    vv.push_back(v);
}

你也可以这样编写循环体:

while(std::getline(file, line))
{
    std::stringstream ss(line);
    std::istream_iterator<int> begin(ss), end;
    std::vector<int> v(begin, end);
    vv.push_back(v);
}

这看起来更短,也更好。或者合并最后两行:
while(std::getline(file, line))
{
    std::stringstream ss(line);
    std::istream_iterator<int> begin(ss), end;
    vv.push_back(std::vector<int>(begin, end));
}

现在不要把它缩短,因为那样会显得很丑。

3
甚至可以这样写:for (std::string line; std::getline(file, line); vv.push_back(std::vector<int>(std::istream_iterator<int>(std::istringstream(line)), std::istream_iterator<int>()))) {} - Kerrek SB
@KerrekSB:哈哈,好主意。我喜欢将“line”变量的作用域最小化的想法。 - Nawaz
迭代器只能从非常量引用构造。很遗憾,您无法从rvalue构造它。 - Kerrek SB
1
@Nawaz:令人印象深刻——不,我不确定我理解了。为什么返回值不再是rvalue?是否可以通过返回其参数的引用的函数更简单地实现相同的效果? - Kerrek SB
1
因此,应该有一个混合模板can_use_as_temporary<T>,从中派生,并公开T & lvalue() { return *this; } - Kerrek SB
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接