473,397 Members | 2,028 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,397 software developers and data experts.

using sstream to read a vector<string>

This works fine, I welcome any views/advices/coding-practices :)
/* C++ Primer - 4/e
*
* Exercise 8.9
* STATEMENT:
* write a program to store each line from a file into a
* vector<string>. Now, use istringstream to read read each line
* from the vector a word at a time.
*
*/

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
void read_file( std::ifstream& infile, std::vector<std::string>&
svec )
{
std::string a_line;
while( std::getline( infile, a_line ) )
{
svec.push_back( a_line );
}
}
/* This program works nearly in 3 steps:

step 1 : we take the path of the file and open it.
step 2 : we read the file into the vector<string>
ansd close it.
step 3 : we use the istringstream to read each line,
a word at a time.
*/
int main()
{
/* step 1 */
std::vector<std::stringsvec;

std::cout << "Enter full path of the file: ";
std::string path_to_file;
std::getline( std::cin, path_to_file );

std::ifstream infile( path_to_file.c_str() );

/* step 2 */
/* check whether file was even opened or not */
if( infile.good() )
{
read_file( infile, svec );
}

/* reading finished, don't forget to close the file */
infile.close();

/* step 3 */
for( std::vector<std::string>::const_iterator iter = svec.begin();
iter != svec.end(); ++iter)
{
std::string a_word;
/* bind that line, to the istringstream */
std::istringstream vector_line( *iter );

while( vector_line >a_word )
{
std::cout << a_word << " ";
}
}

std::cout << std::endl;

return 0;
}

--------- OUTPUT ------------
[arnuld@Arch64 c++]$ g++ -ansi -pedantic -Wall -Wextra ex_08-16.cpp
[arnuld@Arch64 c++]$ ./a.out
Enter full path of the file: /home/arnuld/programming/scratch.txt
;; This buffer is for notes you don't want to save, and for Lisp
evaluation. ;; If you want to create a file, visit that file with C-x
C-f, ;; then enter the text in that file's own buffer.
[arnuld@Arch64 c++]$

Sep 15 '07 #1
6 5674
arnuld wrote:
This works fine, I welcome any views/advices/coding-practices :)
/* C++ Primer - 4/e
*
* Exercise 8.9
* STATEMENT:
* write a program to store each line from a file into a
* vector<string>. Now, use istringstream to read read each line
* from the vector a word at a time.
*
*/

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
void read_file( std::ifstream& infile, std::vector<std::string>&
svec )
{
std::string a_line;
while( std::getline( infile, a_line ) )
{
svec.push_back( a_line );
}
}
Why limit this to 'ifstream'? Why not 'istream'? Then you could
re-use it to read from the standard input as well.
>

/* This program works nearly in 3 steps:

step 1 : we take the path of the file and open it.
step 2 : we read the file into the vector<string>
ansd close it.
step 3 : we use the istringstream to read each line,
a word at a time.
*/
int main()
{
/* step 1 */
std::vector<std::stringsvec;

std::cout << "Enter full path of the file: ";
std::string path_to_file;
std::getline( std::cin, path_to_file );
You should consider asking this question only if 'argv[1]' was not
provided. If it is provided, try assigning it to 'path_to_file':

if (argc 1)
path_to_file = argv[1];
else {
std::cout << "Enter file name: " << std::flush;
std::getline(std::cin, path_to_file);
}

And check the error condition on the 'cin' here -- what if I press
Ctrl-D. You should exit then.
>
std::ifstream infile( path_to_file.c_str() );

/* step 2 */
/* check whether file was even opened or not */
if( infile.good() )
{
read_file( infile, svec );
There is no checking whether you actually read anything, and there
was no error in process (not sure if you want/need that, but might
be useful to tell the user that reading failed in case it has).
}

/* reading finished, don't forget to close the file */
infile.close();

/* step 3 */
for( std::vector<std::string>::const_iterator iter = svec.begin();
iter != svec.end(); ++iter)
{
std::string a_word;
/* bind that line, to the istringstream */
std::istringstream vector_line( *iter );

while( vector_line >a_word )
{
std::cout << a_word << " ";
}
}

std::cout << std::endl;

return 0;
}

--------- OUTPUT ------------
[arnuld@Arch64 c++]$ g++ -ansi -pedantic -Wall -Wextra ex_08-16.cpp
[arnuld@Arch64 c++]$ ./a.out
Enter full path of the file: /home/arnuld/programming/scratch.txt
;; This buffer is for notes you don't want to save, and for Lisp
evaluation. ;; If you want to create a file, visit that file with C-x
C-f, ;; then enter the text in that file's own buffer.
[arnuld@Arch64 c++]$

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Sep 15 '07 #2
You should probably have wstring support as well. Depends on command
line parameter you read stream into
vector<stringor vector<wstring>


Sep 15 '07 #3
In article <11**********************@50g2000hsm.googlegroups. com>,
ge*********@gmail.com says...

[ ... ]
/* step 3 */
for( std::vector<std::string>::const_iterator iter = svec.begin();
iter != svec.end(); ++iter)
{
std::string a_word;
/* bind that line, to the istringstream */
std::istringstream vector_line( *iter );

while( vector_line >a_word )
{
std::cout << a_word << " ";
}
}
I'd probably code this section something like this:

std::copy(std::istream_iterator<std::string>(svec. begin(), svec.end(),
std::ostream_iterator<std::string>(std::cout, " "));

I know it was required in an earlier exercise, but I can't say I'm
terribly enthused about the read_file function either. For reading data
a line at a time, I prefer a proxy to use with std::copy. Using that,
your program can be written something like this:

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

// string proxy that reads a line at a time
class line {
std::string data;
public:
std::istream &read(std::istream &is) {
return std::getline(is, data);
}

operator std::string() const { return data; }
};

std::istream &operator>>(std::istream &is, line &d) {
return d.read(is);
}

// Write the words in a string out as a series of space-separated words.
class write_words {
std::ostream &os_;
public:
write_words(std::ostream &os) : os_(os) {}

void operator()(std::string const &line) {
std::stringstream words(line);

std::copy(std::istream_iterator<std::string>(words ),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(os_, " "));
}
};

// To keep things simple, I'm assuming the filename is passed on the
// command line
int main(int argc, char **argv) {
std::vector<std::stringsvec;

// The following block is vaguely equivalent to read_file
{
std::ifstream in(argv[1]);

std::copy(std::istream_iterator<line>(in),
std::istream_iterator<line>(),
std::back_inserter(svec));
}

std::for_each(svec.begin(), svec.end(), write_words(std::cout));
return 0;
}

Personally, I find this quite a bit more readable. Instead of the
"steps" being encoded as comments, the code itself expresses each step
fairly directly. The one fact that may not be immediately obvious is
that since 'in' is a local variable, it is automagically closed upon
leaving the block in which it was created. I _strongly_ prefer this to
explicitly calling in.close() -- it's much easier to produce exception-
safe code this way. That may not be much of an issue for you yet, but
you're progressing quickly enough that I'd guess it will be soon, and
it's a lot easier if you don't have to re-learn everything when you do.

As an aside, I suppose I should add that for a real program, more error-
checking is clearly needed. You should clearly check that argc>1 before
trying to use argv[1] -- though as Victor (I think it was Victor anyway)
suggested, you might want to use the file specified on the command line
if there was one, and prompt for an input file name otherwise. If you
decide to do that, I'd move the code to do so into a function instead of
leaving it directly in main though, something like:

char *file_name = argv[1];
if (file_name == NULL) // argv[argc] == NULL
file_name = get_file_name(std::cin);

--
Later,
Jerry.

The universe is a figment of its own imagination.
Sep 15 '07 #4
In article <11**********************@50g2000hsm.googlegroups. com>,
ge*********@gmail.com says...

[ ... ]

I've done a bit more playing around with the code I posted earlier. I
wasn't entirely happy about using 'std::for_each' to do what was really
a copy operation. Admittedly, by using 'write_words(std::cout)' in the
for_each, it was fairly apparent what was happening. Nonetheless, I
prefer to copy things with std::copy. The proxy-based approach can be
extended to deal with output just as well as input, so I decided to try
that out:

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

class line {
std::string data;
static const std::string empty;
public:
std::istream &read(std::istream &is) {
return std::getline(is, data);
}

line(std::string const &init) :data(init) {}
line(){}

std::ostream &write(std::ostream &os) const {
std::stringstream x(data);
std::copy(std::istream_iterator<std::string>(x),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(os, " "));
return os;
}

operator std::string() const { return data; }
};

std::istream &operator>>(std::istream &is, line &d) {
return d.read(is);
}

std::ostream &operator<<(std::ostream &os, line const &d) {
return d.write(os);
}

int main(int argc, char **argv) {
std::vector<std::stringsvec;
std::ifstream in(argv[1]);

std::copy(std::istream_iterator<line>(in),
std::istream_iterator<line>(),
std::back_inserter(svec));

std::copy(svec.begin(),
svec.end(),
std::ostream_iterator<line>(std::cout, " "));

return 0;
}

Class line is now something of a misnomer. In reality, 'line' should
probably be left as it was before, and a new class (word_writer or
something like that) created for the output. I didn't bother for now,
mostly because it makes the code longer, and I doubt it makes much
difference in understanding what I've done. I suspect many people
probably find the previous version more understandable than this anyway.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Sep 15 '07 #5
I really don't understand what exactly is going on here :(

Sep 16 '07 #6
In article <11**********************@50g2000hsm.googlegroups. com>,
ge*********@gmail.com says...
I really don't understand what exactly is going on here :(
Sorry 'bout that. I undoubtedly should have included quite a bit more
commentary about the code when I posted it.

As I mentioned previously, our first class is a proxy. It's mostly a
stand-in for std::string, but it changes a few specific things about how
a string acts.

The first thing we change is how we read our special string type.
Instead of reading a word at a time, we read an entire line at a time:

std::istream &read(std::istream &is) {
return std::getline(is, data);
}

That's all this does: make it so line.read(some_istream) reads a whole
line of data into a string.

line(std::string const &init) :data(init) {}

This just lets us initialize a line from a string. It just copies the
string into the line's data member (which is a string, so it doesn't
really change anything).

line(){}

This default ctor would have been there by default, but since we added
the ctor that creates a line from a string, we have to explicitly add it
back in or it won't be there -- and we generally need a default ctor for
anything we're going to put into any standard container.

std::ostream &write(std::ostream &os) const {

std::stringstream x(data);

This much takes the line that we read in, and puts it into a
stringstream.
std::copy(std::istream_iterator<std::string>(x),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(os, " "));

Now we take our stringstream, read out one word at a time, and write it
out to a stream. It's roughly equivalent to code like:

std::string temp;
while ( x >temp) {
os << temp;
os << " ";
}

The big difference is that std::copy can take essentially any pait of
iterators as input, and write those items to essentially any iterator
for the output.

operator std::string() const { return data; }

This is a crucial part of allowing our 'line' class to act like a
string: we allow it to be converted to a string at any time. This
becomes crucial in how we use it below.

std::istream &operator>>(std::istream &is, line &d) {
return d.read(is);
}

std::ostream &operator<<(std::ostream &os, line const &d) {
return d.write(os);
}

These two functions just overload operator>and << to use our member
funtions. They allow code to read a line with code like:

std::cin >some_line;

and it reads an entire line of data instead of just one word.

int main(int argc, char **argv) {
std::vector<std::stringsvec;

std::ifstream in(argv[1]);

This just takes the first name that was passed on the command line and
opens it as a file.

std::copy(std::istream_iterator<line>(in),
std::istream_iterator<line>(),
std::back_inserter(svec));

Now, here's were we get a little bit tricky: even though we have a
vector of string, we use an istream_iterator<line>. That means the
istream_iterator uses the operator>we defined above (which, in turn,
calls line::read) to read in each item from the stream. It then uses
push_back to add that item to svec. This, in turn, uses the operator
string we defined above, so it ends up using the entire string that we
read in (i.e. the entire line we read with readline in line.read().

std::copy(svec.begin(), svec.end(),
std::ostream_iterator<line>(std::cout, " "));

Here we reverse things: we copy the contents of svec out to an
ostream_iterator for std::cout -- but it's an ostream_iterator<line>, so
when it writes each complete line out to std::cout, it does so using
line.write. That, in turn, creates a stringstream and writes out each
word in the line individually instead of writing out the whole line.

Our proxy (line) is pretty close to what most people initially think
they will do with inheritance -- create a new class that acts like the
existing class (string, in this case) except for changing a couple of
specific operations (input and output, in this case). The ctor and cast
operator we provided in line allow us to freely convert between this
type and string whenever we want/need to. In this case, we read in
lines, convert and store them as strings, then convert them back to
lines as we write them out.

Given that we never do anything _else_ with the strings, we could
eliminate all the conversions by creating svec as a vector<lineinstead
of a vector<string>. This, however, would have a substantial
disadvantage if we did much of anything else with our strings -- we'd
have to explicitly provide access to any other string operations, and as
we all know, there are a LOT of string operations. Providing the
conversions allows us to support all the string operations with very
little work.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Sep 16 '07 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: dalbosco | last post by:
Hello, I am new to STL and I've written the following code which crash. Could anyone tell me why this code crash? Thanks by advance. -- J-F #include <iostream>
2
by: ehui928 | last post by:
hi, everybody I am a newbie in STL. When I compile the following program under gcc4.0, I got a the following errors. I wonder whether the form of list< vector<string> > is correct in STL ? //...
5
by: Martin Jørgensen | last post by:
Hi, The piece of code I'm struggling with is so simple, that I hope nobody wants a complete example for answering the question: -------- string color_line; int data_type = 0; for(...
10
by: Shafik | last post by:
Hello, I am new to C++. I know the reason is probably template instantiation problems ... but what's the *real* reason I cannot declare a: vector<stringv = vector<string>(4); Thanks!...
2
by: Rockair | last post by:
hi! there is a class: class card { static vector<string> names; //... };
6
by: Mr. K.V.B.L. | last post by:
I want to start a map with keys but an empty vector<string>. Not sure what the syntax is here. Something like: map<string, vector<string MapVector; MapVector.insert(make_pair("string1",...
42
by: barcaroller | last post by:
In the boost::program_options tutorial, the author included the following code: cout << "Input files are: " << vm.as< vector<string() << "\n"; Basically, he is trying to print a vector...
9
by: barcaroller | last post by:
1. If I pass pointers (char*) as iterators to an STL algorithm and the return value is an iterator, can I convert that iterator to a pointer? If yes, how? 2. What is the internal...
0
by: Thomas | last post by:
Hi, I have a C++-Class "compiled" with SWIG into a Python-Modul. It works fine. But there's is a problem with the C++-Returntype vector<string>. If I call in my compiled Python-Module a certain...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.