57

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

4

8 回答 8

36

编辑:这个技巧已经由其他人在之前的帖子中发布过。

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;
}
于 2010-02-18T20:10:58.580 回答
25

标准库不提供执行此操作的迭代器(尽管您可以自己实现类似的东西),但您可以简单地使用getline函数(不是 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;
}
于 2010-02-18T20:09:57.633 回答
7

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

#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();
}

编辑:曼努埃尔更快。

于 2010-02-18T21:58:02.860 回答
3

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

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

于 2010-02-18T20:11:32.153 回答
1

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

代码.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;
}

输入.txt:

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

输出:

ahhhh test
bhhhh second test
于 2010-02-18T21:57:48.693 回答
1

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

#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);
于 2019-10-10T03:40:51.057 回答
1

也可以使用基于范围的 for 循环

// 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_;
};
于 2021-12-18T01:51:11.183 回答
0

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

首先是直接从相关线程中 Jerry 的回答中复制的片段:

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”)完全相同。

于 2019-04-08T06:21:56.957 回答