1

In the style of What is the best way to read an entire file into a std::string in C++? I want to ask a similar question, except that I want my output to be an array/STL container containing the lines in a text file.

C#/.net has the rather useful File.ReadAllLines() utility function, what would a good C++ (STL) version look like?

Community
  • 1
  • 1
Mr. Boy
  • 52,885
  • 84
  • 282
  • 517
  • His requirements were more specific than mine! – Mr. Boy Jan 28 '13 at 11:52
  • 1
    possible duplicate of [How do I iterate over cin line by line in C++?](http://stackoverflow.com/questions/1567082/how-do-i-iterate-over-cin-line-by-line-in-c) – Jerry Coffin Jan 28 '13 at 14:42
  • Is it a dupe? I have a fixed-sized file to work from. Would @Nawaz's answer work on `cin`? – Mr. Boy Jan 29 '13 at 09:21
  • @John: Yes. My answer would work on `std::cin` also. Here is online demo : http://ideone.com/d1Ng1h – Nawaz Jan 29 '13 at 10:28

4 Answers4

6

In C++, you could do this.

  • Define a struct as:

    struct line : std::string 
    {
       friend std::istream & operator >> (std::istream & in, line & ln)
       {
          return std::getline(in, ln);
       }
    };
    
  • then do this:

    std::ifstream file("file.txt");
    std::istream_iterator<line> begin(file), end;
    std::vector<std::string> allLines(begin, end);
    
  • Done!

With this approach, you can directly work with iterator-pair begin and end. No need to use std::vector<std::string>. Note that line can implicitly convert into std::string. So you can use begin and end with the Standard algorithms.

For example,

  • Find the longest line:

    auto cmp = [](line const &a, line const& b) { return a.size() < b.size(); };
    std::string longestLine = *std::max_element(begin, end, cmp);
    
  • Count the lines whose length is greater than 10:

    auto cmp = [](line const &a) { return a.size() > 10 ; };
    size_t count = std::count_if(begin, end, cmp);
    
  • So on. In this way, you can work directly work with begin and end. No need to use std::vector. Save memory.

Hope that helps.

Nawaz
  • 327,095
  • 105
  • 629
  • 812
4

The standard idiom:

#include <fstream>
#include <string>
#include <vector>
#include <utility>

std::ifstream infile("file.txt");
std::vector<std::string> v;

for (std::string line; std::getline(infile, line); )
{
    v.push_back(std::move(line));
}
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
3
std::ifstream ifs(name);
std::vector<std::string> lines;
std::string line;
while (std::getline(ifs, line))
    lines.push_back(line);
rodrigo
  • 79,651
  • 7
  • 121
  • 162
1

This is my attempt at @Nawaz's idea. I've avoided inheritance from std::string, as I felt a little queasy about it:

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

struct Line 
{
    operator const std::string&() const {return string;}
    std::string string;
};

std::istream& operator>>(std::istream& in, Line& line)
{
    return std::getline(in, line.string);
}

int main()
{
    std::istream_iterator<Line> begin(std::cin), end;
    std::vector<std::string> allLines(begin, end);

    std::cout << allLines.size() << " lines read from file:" << std::endl;
    std::copy(allLines.begin(), allLines.end(),
          std::ostream_iterator<std::string>(std::cout, "|"));
    return 0;
}
Peter Wood
  • 21,348
  • 4
  • 53
  • 90
  • With this, the user wouldn't be able to write `line.size()` for example. And I don't find any reason why `line` shoudn't be derived from `std::string`. Or say why `basic_line` should not be derived from `basic_string`. `basic_line` *is* `basic_string`. Only that they're not polymorphic. – Nawaz Jan 29 '13 at 13:10
  • I would only use `Line` to marshal data into `std::string`s. – Peter Wood Jan 29 '13 at 13:17
  • I know that already from your answer. It is just that I don't find any convincing reason why `class basic_line : public std::basic_string {};` is not a good idea. I feel that nobody has done it so far, that is why many find it in the must-be-avoided category. – Nawaz Jan 29 '13 at 13:19
  • There are [two](http://stackoverflow.com/questions/4205050/inheriting-and-overriding-functions-of-a-stdstring) [questions](http://stackoverflow.com/questions/6006860/why-should-one-not-derive-from-c-std-string-class) about not deriving from `std::string`. – Peter Wood Jan 29 '13 at 13:22
  • Nothing in those answers applies to this situation. – Nawaz Jan 29 '13 at 15:13
  • @PeterWood: Welllll.... polymorphism and inheritance are different things. It's true that there are no virtual functions in `std::basic_string`, but... so what? You only need a virtual destructor if you are handling more-derived objects through base interfaces. But no such thing is being done here. In other words, a `line` *is-not* a `string`. It's a line, and it just happens to be identical to a string. – Kerrek SB Jan 29 '13 at 15:13
  • I'm worried about object slicing. I know it's unlikely to cause a problem as `list` has no member data, but are there any dangers? – Peter Wood Jan 29 '13 at 22:18