Reputation: 697
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
?
Upvotes: 1
Views: 1809
Reputation: 15277
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;
}
Upvotes: 1
Reputation: 153840
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.
Upvotes: 3