1

I want to read a text file, line by line, using istream_iterator, but it fails when there is a white space in the line.

This is a sample code:

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

int main(int argc, char** argv)
{
  if (argc != 2) {
    std::cout << argv[0] << " <file-name>" << std::endl;
    return 10;
  }

  std::ifstream _in(argv[1]);
  if (!_in.good()) {
      std::cout << "error reading " << argv[1] << std::endl;
      return 20;
  }

  _in.unsetf(std::ios_base::skipws);

  std::istream_iterator<std::string> _ite(_in);
  std::istream_iterator<std::string> _end;
  std::string _s(*_ite);

  std::cout << "line read " << _s << std::endl;
}

For example, in this input file: 1;3.14;bla bla -3;0.923;let me go

The first string read is 1;3.14;bla

Is there a way to do it, or should I give up and use getline?

1
  • 4
    If you need to "read a text file, line by line", you're in luck: this happens to be exactly what std::getline does. Use the right tool, for the right job. And the right tool for this job is not a stream iterator but std::getline. Commented Aug 15, 2019 at 1:41

2 Answers 2

3

When a std::string is read it reads until the first space character is found. What qualifies as a space character is defined by the stream’s std::locale, more precisely its std::ctype<char> facet. You can create a std::ctype<char> facet which considers only ’\n’ as a space, create a std::locale object containing that facet, imbue() the stream with the corresponding std::locale and it should work.

Sign up to request clarification or add additional context in comments.

Comments

1

No. Do not give up. Use the C++ approach. Although I will also use std::getline, I think it is rather C-Style. And therefor I will wrap this functrion in a proxy class.

I find your idea good, to use a std::istream_iterator. This is the "more modern" C++ way of doing things. And the big advantage is that you can use the istream_iterator in algorithms.

The only problem to solve is, to implement the abstract model of a "line" into a class. And as I will show below that is fairly easy.

Using a proxy class is the standard approach and you will find a lot of examples here on SO doing exactly that.

Please see:

#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <ios>


std::istringstream testFile{R"(Line1Word1 Line1Word2 Line1Word3 
Line2Word1 Line2Word2
Line3Word1 Line3Word2 Line3Word3  Line3Word4
Line4Word1 Line4Word2 Line4Word3
)"};

struct Line      // ! This is a proxy for the input_iterator and output iterator! 
{   
    // Input function. Read on line of text file and split it in columns
    friend std::istream& operator>>(std::istream& is, Line& line) {
        return std::getline(is, line.lineTemp ); 
    }

    // Output function. 
    friend std::ostream& operator<<(std::ostream& os, const Line& line) {
        return os <<  line.lineTemp; 
    }

    // cast to needed result
    operator std::string() const { return lineTemp; }  
    // Temporary Local storage for line
    std::string lineTemp{};  
};


int main()
{
    std::cout << "\n\nTest 1. Read all lines into a vector:\n";
    std::vector<std::string> allLines {std::istream_iterator<Line>(testFile),std::istream_iterator<Line>() };
    std::copy(allLines.begin(), allLines.end(), std::ostream_iterator<std::string>(std::cout, "\n"));

    std::cout << "\n\nTest 2: Display fist 2 in file\n";
    testFile.clear(); testFile.seekg(0,std::ios::beg);
    std::copy_n(std::istream_iterator<Line>(testFile),2,std::ostream_iterator<std::string>(std::cout, "\n"));

    testFile.clear(); testFile.seekg(0,std::ios::beg);
    std::cout << "\n\nTest 3: Number of lines in File\n"
        << std::distance(std::istream_iterator<Line>(testFile),std::istream_iterator<Line>());

    std::cout << "\n\nTest 4: Use iterator separately\n";
    testFile.clear(); testFile.seekg(0,std::ios::beg);
    // Define the iterator
    std::istream_iterator<Line> lineIterator(testFile);
    // Dereference iterator
    std::cout << *lineIterator << "\n";

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.