Re: Reading items from a text file to a vector
zr wrote:
Hi,
i need to read a text file which contains a list of items, all of type
ItemType, separated by whitespace and newlines. For each line, there
will be a vector<ItemType> object that will store the integers read in
that line. There will be a single vector<vector<ItemType>> object that
will stores all of the vector<ItemType> objects mentioned in the
previous sentence.
I wrote a quick implementation and it seems to be working, but i would
like to hear some opinions on what may be incorrect or may be
improved, especially exception and runtime error handling. Assume that
the required operator>> in code line 13 is defined.
TIA
1 template <class ItemType>
2 void collect(istream& source, vector<vector<ItemType>>& db) {
3 while (source.good()) {
4 string s;
5 getline(source, s, '\n');
6 istringstream iss(s);
7 if (iss.bad() || 0==s.size())
8 break;
9 vector<ItemType> t;
10
11 while (iss.good()) {
12 ItemType item;
13 iss >> item;
14 t.push_back(item);
15 }
16 db.push_back(t);
17 }
18}
You have received some suggestions so far. The following is radically
different: (a) globally and (b) locally.
(a) The two problems of converting a single line and converting a whole file
are intermingled in your solution. I suggest taking those issues apart.
Also, you deal with vectors of vectors specifically. I think, one can be
more general.
(b) Just for fun, I used iterators and standard algorithms. Two different
methods of error reporting are employed: exceptions if there is no stream
to return or the status bits of the stream if present.
Here goes:
#include <iostream>
#include <istream>
#include <string>
#include <iterator>
#include <vector>
#include <algorithm>
#include <sstream>
#include <stdexcept>
struct line : public std::string {};
std::istream & operator>> ( std::istream & istr, line & l ) {
return ( std::getline( istr, l ) );
}
template < typename Sequence >
Sequence scan_line ( std::string const & l ) {
std::istringstream istr ( l );
Sequence result;
typedef typename Sequence::value_type value_type;
std::copy( std::istream_iterator< value_type >( istr ),
std::istream_iterator< value_type >(),
std::back_inserter( result ) );
if ( ! istr.eof() || istr.bad() ) {
throw ( std::invalid_argument( "line invalid\n" ) );
}
return ( result );
}
template < typename Sequence >
std::istream & collect ( std::istream & istr, Sequence & seq ) {
typedef typename Sequence::value_type value_type;
std::transform( std::istream_iterator< line >( istr ),
std::istream_iterator< line >(),
std::back_inserter( seq ),
& scan_line< value_type > );
return ( istr );
}
int main ( void ) {
std::vector< std::vector<int> > ivv;
try {
collect( std::cin, ivv );
if ( std::cin.eof() && ! std::cin.bad() ) {
for ( unsigned long n = 0; n < ivv.size(); ++n ) {
for ( unsigned long m = 0; m < ivv[n].size(); ++m ) {
std::cout << ivv[n][m] << " ";
}
std::cout << "\n";
}
} else {
std::cerr << "File format error\n";
}
}
catch ( std::invalid_argument const & i ) {
std::cerr << i.what();
}
}
BTW: I do not claim that this is "better". I just think you should know that
there are many ways to go about this.
Best
Kai-Uwe Bux