【问题标题】:Is there a C++ iterator that can iterate over a file line by line?是否有可以逐行迭代文件的 C++ 迭代器?
【发布时间】:2011-01-18 12:09:58
【问题描述】:

我想要一个 istream_iterator 风格的迭代器,它将文件的每一行作为字符串而不是每个单词返回。这可能吗?

【问题讨论】:

  • 我猜你总是可以像 Matteo Italia 所说的那样使用 getline() 函数编写自己的函数。
  • @Jerry:该​​线程包含答案。但问题完全不同。
  • @UnbleBens:问题的措辞不同,但并没有明显不同。
  • @Jerry:谢谢!我将采用您发布到另一个问题的解决方案。但我同意 UncleBens 的观点,我根本没有问过这个问题。我特别想要“一个迭代器”作为我传递给它的函数,它需要一个开始和一个结束。

标签: c++ file iterator newline line


【解决方案1】:

编辑:这个技巧已经被其他人in a previous thread发布了。

std::istream_iterator 做你想做的事很容易:

namespace detail 
{
    class Line : std::string 
    { 
        friend std::istream & operator>>(std::istream & is, Line & line)
        {   
            return std::getline(is, line);
        }
    };
}

template<class OutIt>
void read_lines(std::istream& is, OutIt dest)
{
    typedef std::istream_iterator<detail::Line> InIt;
    std::copy(InIt(is), InIt(), dest);
}

int main()
{
    std::vector<std::string> v;
    read_lines(std::cin, std::back_inserter(v));

    return 0;
}

【讨论】:

  • @thehouse - 你的意思是什么编码标准?我认为使用任意类作为基类没有任何问题,只要它没有在多态上下文中使用。例如,如果我做了类似string * ptr = new Line; delete ptr; 这样的事情,我的答案中的继承方案会很危险,但这里不是这样
  • 这是错误的,完全错误的,并且在原始示例中并非如此(作者明智地选择了Composition)。 @Manuel 证明我没有人会在多态上下文中使用它们...我在等待。
  • 你能解释一下为什么我们需要从字符串类继承吗?
  • 最后我使用了这种方法,但将std::string 存储为成员而不是继承 - 个人喜好问题。
  • 值得指出的是,继承是调整接口的好方法。它易于阅读和理解。如果没有引入新成员,那么基于堆的逻辑不会毁了你。再复杂的事情就是自找麻烦
【解决方案2】:

标准库不提供执行此操作的迭代器(尽管您可以自己实现类似的东西),但您可以简单地使用 getline function(不是 istream 方法)从输入流中读取整行到 C++ 字符串。

例子:

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    ifstream is("test.txt");
    string str;
    while(getline(is, str))
    {
        cout<<str<<endl;
    }
    return 0;
}

【讨论】:

  • 它是否处理不同平台(windows/unix/mac)的 eol 字符差异?
  • 流对象中已经处理了这种差异:当您以文本模式打开文件时(如果您未指定 ios::binary 标志,则为默认值)流自动转换特定于平台的 eol到普通的\n.
  • 我们使用的 COM istream 对 EOL 的处理方式不同。解析 dos 文件有效,但解析 UNIX(无 LF)文件导致它被当作一大行处理。
  • @Kelly:呃,等等; std::istream 仅正确转换当前平台的 EOL 本机,对其他人来说可能什么都不做。另外,现在您谈论的是 COM istream,因此您应该参考它的文档。
【解决方案3】:

这里有一个解决方案。该示例在每行末尾使用@@ 打印输入文件。

#include <iostream>
#include <iterator>
#include <fstream>
#include <string>

using namespace std;

class line : public string {};

std::istream &operator>>(std::istream &is, line &l)
{
    std::getline(is, l);
    return is;
}

int main()
{
    std::ifstream inputFile("input.txt");

    istream_iterator<line> begin(inputFile);
    istream_iterator<line> end;

    for(istream_iterator<line> it = begin; it != end; ++it)
    {
        cout << *it << "@@\n";
    }

    getchar();
}

编辑:曼努埃尔更快。

【讨论】:

    【解决方案4】:

    您可以编写自己的迭代器。这并不难。 迭代器只是一个类,(简单地说)在其上定义了增量和 * 运算符。

    查看http://www.drdobbs.com/cpp/184401417,开始编写您自己的迭代器。

    【讨论】:

    • @thehouse:您可能还想查看boost::iterator_facade,它在几个核心功能方面实现了完整的 STL 迭代器概念。
    【解决方案5】:

    您可以使用 istreambuf_iterator 代替 istream_iterator。它不会忽略像 istream_iterator 这样的控制字符。

    code.cpp:

    #include <iterator>
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main()
    {
        ifstream file("input.txt");
    
        istreambuf_iterator<char> i_file(file);
    
        istreambuf_iterator<char> eof;
    
        std::string buffer;
        while(i_file != eof)
        {
            buffer += *i_file;
            if(*i_file == '\n')
            {
                std::cout << buffer;
                buffer.clear();
            }
            ++i_file;
        }
    
        return 0;
    }
    

    input.txt:

    ahhhh test *<-- There is a line feed here*
    bhhhh second test *<-- There is a line feed here*
    

    输出:

    ahhhh test
    bhhhh second test
    

    【讨论】:

      【解决方案6】:

      这是一个使用boost::tokenizer 的非常干净的方法。这将返回一个提供begin()end() 成员函数的对象;完整的接口请看tokenizer class的文档。

      #include <boost/tokenizer.hpp>
      #include <iostream>
      #include <iterator> 
      
      
      using istream_tokenizer = boost::tokenizer<boost::char_separator<char>,
                                                 std::istreambuf_iterator<char>>;
      
      istream_tokenizer line_range(std::istream& is);
      {
          using separator = boost::char_separator<char>;
      
          return istream_tokenizer{std::istreambuf_iterator<char>{is},
                                   std::istreambuf_iterator<char>{},
                                   separator{"\n", "", boost::keep_empty_tokens}};
      }
      

      这会将char 硬编码为流的字符类型,但这可以模板化。

      函数可以使用如下:

      #include <sstream>
      
      std::istringstream is{"A\nBB\n\nCCC"};
      
      auto lines = line_range(is);
      std::vector<std::string> line_vec{lines.begin(), lines.end()};
      assert(line_vec == (std::vector<std::string>{{"A", "BB", "", "CCC"}}));
      

      当然,它也可以与通过打开文件创建的std::ifstream 一起使用:

      std::ifstream ifs{"filename.txt"};
      auto lines = line_range(ifs);
      

      【讨论】:

      • 很高兴看到分词器的答案
      【解决方案7】:

      在上面引用的相关线程 iterate-over-cin-line-by-line 中,Jerry Coffin 描述了“另一种可能性(它)使用了大多数人几乎不知道存在的标准库的一部分。”以下应用该方法(这是我一直在寻找的)来解决当前线程中要求的逐行迭代文件问题。

      首先是直接从相关线程中 Jerry 的回答中复制的 sn-p:

      struct line_reader: std::ctype<char> {
      line_reader(): std::ctype<char>(get_table()) {}
      static std::ctype_base::mask const* get_table() {
          static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());
          rc['\n'] = std::ctype_base::space;
          return &rc[0];
      }}; 
      

      现在,为 ifstream 注入 Jerry 描述的自定义语言环境,然后从 infstream 复制到 ofstream。

      ifstream is {"fox.txt"};
      is.imbue(locale(locale(), new line_reader()));
      istream_iterator<string> ii {is};
      istream_iterator<string> eos {};
      
      ofstream os {"out.txt"};
      ostream_iterator<string> oi {os,"\n"};
      
      vector<string> lines {ii,eos};
      copy(lines.begin(), lines.end(), oi);
      

      输出文件(“out.txt”)将与输入文件(“fox.txt”)完全相同。

      【讨论】:

        【解决方案8】:

        也可以使用range-based for loop:

        // Read from file.
        std::ifstream f("test.txt");
        for (auto& line : lines(f))
          std::cout << "=> " << line << std::endl;
        
        // Read from string.
        std::stringstream s("line1\nline2\nline3\n\n\nline4\n\n\n");
        for (auto& line : lines(s))
          std::cout << "=> " << line << std::endl;
        

        lines 的定义方式如下:

        #include <string>
        #include <iterator>
        #include <istream>
        
        struct line_iterator {
          using iterator_category = std::input_iterator_tag;
          using value_type = std::string;
          using difference_type = std::ptrdiff_t;
          using reference = const value_type&;
          using pointer = const value_type*;
        
          line_iterator(): input_(nullptr) {}
          line_iterator(std::istream& input): input_(&input) { ++*this; }
        
          reference operator*() const { return s_; }
          pointer operator->() const { return &**this; }
        
          line_iterator& operator++() {
            if (!std::getline(*input_, s_)) input_ = nullptr;
            return *this;
          }
        
          line_iterator operator++(int) {
            auto copy(*this);
            ++*this;
            return copy;
          }
        
          friend bool operator==(const line_iterator& x, const line_iterator& y) {
            return x.input_ == y.input_;
          }
        
          friend bool operator!=(const line_iterator& x, const line_iterator& y) {
            return !(x == y);
          }
        
         private:
          std::istream* input_;
          std::string s_;
        };
        
        struct lines {
          lines(std::istream& input): input_(input) {}
        
          line_iterator begin() const { return line_iterator(input_); }
          line_iterator end() const { return line_iterator(); }
        
         private:
          std::istream& input_;
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-02-23
          • 2018-02-16
          • 2018-01-29
          • 1970-01-01
          • 2015-12-07
          • 1970-01-01
          • 1970-01-01
          • 2019-11-12
          相关资源
          最近更新 更多