Reading items from a text file to a vector

Z

zr

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}
 
R

red floyd

zr said:
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.
Don't use line numbers in your posts. It makes it difficult for
readers to cut&paste your code.
Correction on your line2 and on your lines 11-15.

1 template <class ItemType>
2 void collect(istream& source, vector<vector<ItemType>>& db) {
vector said:
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 }
for (ItemType item; iss >> item; )
t.push_back(item);
 
R

red floyd

red said:
Don't use line numbers in your posts. It makes it difficult for
readers to cut&paste your code.
Correction on your line2 and on your lines 11-15.

Forgot the correction on 3-5.
 
J

James Kanze

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.

As you've posted it, it won't compile:). But in fact, it isn't
guaranteed to work, even with the correction to the syntax.
1 template <class ItemType>
2 void collect(istream& source, vector<vector<ItemType>>& db) {

">>" is a shift left operator, and doesn't close two open template
lists; you need "> >" (unless your compiler has already
implemented this feature of the next version of the standard).
3 while (source.good()) {

And this is an error; source.good() does NOT mean that the next
input is guaranteed to succeed. (In practice, what will
probably happen is that you'll get an extra, empty list at the
end of your results.)
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()) {

Same thing here. Most of the time, this will work anyway, but
if you have a line which contains trailing white space, it may
result in an additional default constructed item at the end of
the list.
12 ItemType item;
13 iss >> item;
14 t.push_back(item);
15 }
16 db.push_back(t);
17 }
18}

In general, you should always test *after* reading from a
stream, to know if the input succeeded or failed. And be wary
of the names of the status functions in the streams; they are
very misleading. (I've never found a use for good(), for
example.) Taking this into account, your code should probably
look something like:

std::string s ;
while ( std::getline( source, s ) ) {
std::istringstream iss( s ) ;
std::vector< ItemType >
t ;
ItemType item ;
while ( iss >> item ) {
t.push_back( item ) ;
}
db.push_back( t ) ;
}

In addition, you might want to think about other possible input
errors: what happens if an item in the file is misformatted.
(You can typically detect this by checking iss.eof() after the
inner loop above; if input failed and you don't have eof() or
bad(), then you have a formatting error.)
 
K

Kai-Uwe Bux

zr said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top