Wednesday, September 7, 2016

Define: stringstream

stringstream

library

#include <sstream>
The stringstream class is extremely useful in parsing contest input. It associates a string object with a stream allowing you to read from the string as if it were a stream (like cin).
The basic methods for stringstream are:

  • clear () -- clears the error bits in the stringstream object so that you can use it again. You need to call this each time you start parsing a new string with the stringstream object. If you don't, the object could still be in a bad sate and you may not be able to read anything from the stringstream (even if you change its associated string)..
  • str () -- returns the string associated with the stringstream object..
  • str (s) -- associates the string s to the stringstream object...
  • operator <<  -- add a string to the stringstream object...
  • operator >>  -- read something from the stringstream object...


As an example, suppose you were told that each test case will be a single line of input with a series of numbers on it. You can't use a bunch of cin's to read the numbers because you don't know how many of them there are. You have to read off the line using getline and then parse the input line. One way would be to scan through the input line looking for spaces, call the substr method of the string class to get the substring for each number, convert it to a C string (the member method c_str()) and then call atoi on that. A far easier way is to use stringstream like this:


#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int
main (void)
{
	stringstream ss;
	int foo;
	string inp;
	while (getline (cin, inp))
       {
		ss.clear ();
		ss.str ("");
		ss << inp;
		while (ss >> foo)
               {
			cout << foo << endl;
		}
	}
	return 0;
}

Here's a more difficult example. Suppose your input is a series of pairs of the form (int,str). Pairs are separated in the input by spaces. If there are no embedded spaces in a given pair, you can read and parse them by:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int
main (void)
{
	stringstream ss;
	string inp, w;
	int num;
	char ch;

	while (cin >> inp) {
		inp.resize (inp.size () - 1);
		ss.clear ();
		ss.str ("");
		ss << inp;
		ss >> ch >> num >> ch >> w;
		cout << num << " <--> " << w << endl;
	}
	return 0;
}


Note in the above examples that clear() and str ("") were always called before using the stringstream object. These two method calls are needed to reset the internal state of the stringstream object.
clear() is needed to reset the internal error flags. If this is not called, the stringstream object may think it's in an error state (such as EOF), even if the underlying string has been changed.
str ("") is needed to clear out the old string. By calling the stream insertion operator, you are adding to the internal string kept by the stringstream object. This feature is useful if you are building up a string from variables and other strings, but can cause your programs to reparse old data.



StringStream Tutorial

You can think of a stringstream as a file loaded into something resembling a string, or alternatively, as a sort of string that you can write to and read from like a file. It's not exactly either of those things, but read on and it should become clearer...

A stringstream works essentially the same as an input/output file stream. You need the preprocessor directive #include <sstream>, declare a stringstream just like an fstream, for example
stringstream ss;
and, like an fstream or cout, you can write to it:
ss << myString; or 
ss << myCstring; or
ss << myInt;, or float, or double, etc.

and you can read from it:
ss >> myChar; or
ss >> myCstring; or
ss >> myInt; This is also an easy way to convert strings of digits into ints, floats or doubles.

You can get the entire contents of the stringstream as a single C++ string:
string s = ss.str();

And it also inherits many other members from istream and ostream like get, getline, read, write, put, ... In this code I used the stringstream as an intermediate step between an input (text) file and an int array to help deal with the fact that the data within each line of the file was separated by commas, but the lines are separated only by newlines.

Following is a sample program using a stringstream to read numbers from a csv file named "input.txt" into a 6 row by 5 column int array and then prints the array.

It can take data from an input (text) file that looks like this:

11,3,10,3,1
21,10,10,3,10
31,,,,
40,0,0,1,10
51,1,0,2,2
610,1,0,1,10
and this is what ends up in the array:
11 3 10 3 1
21 10 10 3 10
31 0 0 0 0
40 0 0 1 10
51 1 0 2 2
610 1 0 1 10


It uses both forms of the istream getline member. Both of them take characters from the stream, copy them into a char array and terminate them with a '\0'.
The first one reads up to n-1 characters or until it reaches a newline or eof:
istream& getline (char* s, streamsize n );
The second one reads up to n-1 characters or until it reaches the character specified as 'delim' or eof:
istream& getline (char* s, streamsize n, char delim );


01#include <iostream>
02#include <fstream>
03#include <sstream>
04using namespace std;
05 
06const int ROWS = 6;
07const int COLS = 5;
08const int BUFFSIZE = 80;
09 
10int main() {
11  int array[ROWS][COLS];
12  char buff[BUFFSIZE]; // a buffer to temporarily park the data
13  ifstream infile("input.txt");
14  stringstream ss;
15  forint row = 0; row < ROWS; ++row ) {
16    // read a full line of input into the buffer (newline is
17    //  automatically discarded)
18    infile.getline( buff,  BUFFSIZE );
19    // copy the entire line into the stringstream
20    ss << buff;
21    forint col = 0; col < COLS; ++col ) {
22      // Read from ss back into the buffer.  Here, ',' is
23      //  specified as the delimiter so it reads only until
24      //  it reaches a comma (which is automatically
25      //  discarded) or reaches the 'eof', but of course
26      //  this 'eof' is really just the end of a line of the
27      //  original input.  The "6" is because I figured
28      //  the input numbers would be 5 digits or less.
29      ss.getline( buff, 6, ',' );
30      // Next, use the stdlib atoi function to convert the
31      //  input value that was just read from ss to an int,
32      //  and copy it into the array.
33      array[row][col] = atoi( buff );
34    }
35    // This copies an empty string into ss, erasing the
36    //  previous contents.
37    ss << "";
38    // This clears the 'eof' flag.  Otherwise, even after
39    //  writing new data to ss we wouldn't be able to
40    //  read from it.
41    ss.clear();
42  }
43  // Now print the array to see the result
44  forint row = 0; row < ROWS; ++row ) {
45    forint col = 0; col < COLS; ++col ) {
46      cout << array[row][col] << " ";
47    }
48    cout << endl;
49  }
50  infile.close();

51}

The above code was set up explicitly to load data into a 6x5 array because that's what a particular application required, but it can be easily modified to read an entire input file of indeterminate length using a while loop, i.e.
while( infile.getline( buff, 50 ) ) , containing lines with any number of comma-separated values (as long as a big enough buffer is provided). Here's an example of that (it only prints the first 10 columns of the first 10 rows of the array so as not to fill your screen with 0's:


01#include <iostream>
02#include <fstream>
03#include <sstream>
04using namespace std;
05 
06const int ROWS = 100;
07const int COLS = 80;
08const int BUFFSIZE = 80;
09 
10int main() {
11  // The array must be big enough to fit the input data.
12  int array[ROWS][COLS] = {0};
13  int row, col;
14  char buff[BUFFSIZE]; // a buffer to temporarily park the data
15  ifstream infile("input.txt");
16  stringstream ss;
17  // Read input into the buffer a line at a time, until
18  //  end of file is reached (newlines are automatically
19  //  discarded).  The buffer must be big enough to fit
20  //  an entire line from the file.
21  // Notice that while reading from the file we check how
22  //  many rows have been read, to avoid writing beyond
23  //  the end of the array.
24  row = 0;
25  while( infile.getline( buff,  BUFFSIZE ) && row < ROWS ) {
26    // copy the entire buffered line into the stringstream
27    ss << buff;
28    // Read from ss back into the buffer.  Now, ',' is
29    //  specified as the delimiter so it reads only until
30    //  it reaches a comma (which is automatically
31    //  discarded) or reaches the 'eof', but of course
32    //  this 'eof' is really just the end of a line of the
33    //  original input.  The "10" means this will handle
34    //  input numbers of 9 digits or less.
35    //  While reading from the stringstream, we check
36    //  how many columns have been read to avoid
37    //  writing past the end of the array.
38    col = 0;
39    while( ss.getline( buff, 10, ',' ) && col < COLS ) {
40      // Next, use the stdlib atoi function to convert the
41      //  input value that was just read from ss to an int,
42      //  and copy it into the array.
43      array[row][col] = atoi( buff );
44      ++col;
45    }
46    // This copies an empty string into ss, erasing the
47    //  previous contents.
48    ss << "";
49    // This clears the 'eof' flag.  Otherwise, even after
50    //  writing new data to ss we wouldn't be able to
51    //  read from it.
52    ss.clear();
53    ++row;
54  }
55  // Now print the array to see the result
56  forint _row = 0; _row < 10; ++_row ) {
57    forint _col = 0; _col < 10; ++_col ) {
58      cout << array[_row][_col] << " ";
59    }
60    cout << endl;
61  }
62  infile.close();
63}
















....

No comments:

Post a Comment