views:

564

answers:

5

I would like to get an istream_iterator-style iterator that returns each line of the file as a string rather than each word. Is this possible?

+4  A: 

The standard library does not provide iterators to do this (although you can implement something like that on your own), but you can simply use the getline function (not the istream method) to read a whole line from an input stream to a C++ string.

Example:

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

using namespace std;

int main()
{
    ifstream is;
    string str;
    is.open("test.txt");
    while(is)
    {
        getline(is, str);
        reverse(str.begin(), str.end());
        cout<<str.c_str()<<endl;
    }
    return 0;
}
Matteo Italia
+6  A: 

EDIT: This same trick was already posted by someone else in a previous thread.

It is easy to have istream_iterator do what you want:

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;
}
Manuel
Does inheriting from std::string here not violate Rule 35 of the C++ Coding Standard: "Avoid inheriting from classes that were not designed to be base classes"?
thehouse
@thehouse - What coding standard do you mean? I don't think there's anything wrong with using an arbitrary class as base provided that it's not used in a polymorphic context. For instance, the inheritance scheme in my answer would be dangerous if I made things like `string * ptr = new Line; delete ptr;` but that's not the case here
Manuel
@Manuel: Herb Sutter and Andrei Alexandrescu in their book C++ Coding Standards state that "using a standalone class as a base is a serious design error and should be avoided". They go on to mention string explicitly as a bad class to inherit from. Their argument revolves around the fact that you have to do one set of things to get a base class to work safely and a contradictory set of things to make concrete classes safe.
thehouse
It is wrong, completely wrong, and was not so in the original example (the author wisely chose `Composition` instead). `@Manuel` prove me no-one will use them in a polymorphic context... I am waiting.
Matthieu M.
@Matthieu - happy now?
Manuel
Not really, but it's better I will admit it. I am sorry but I don't like inheritance in general... and certainly private inheritance when composition would do. Guess we all have our habits...
Matthieu M.
+3  A: 

You could write your own iterator. It's not that hard. An iterator is just a class on which (simply speaking) the increment and * operators are defined.

Look at http://www.drdobbs.com/cpp/184401417 to get started writing your own iterators.

Patrick
@thehouse: you might also want to check out `boost::iterator_facade`, which implements the full STL iterator concept in terms of a few core functions.
Emile Cormier
A: 

You can use istreambuf_iterator instead of istream_iterator. It doesn't ignore control characters like 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*

output:

ahhhh test
bhhhh second test
coelhudo
+1  A: 

Here is a solution. The exemple print the input file with @@ at the end of each line.

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

Edit : Manuel has been faster.

Rexxar
@Rexxar - My bad, I deleted it and then undeleted it as community wiki. Sorry about that.
Manuel