Clear and reuse a stringstream for multiple conversions - c++

I was working on a Hacker Rank assignment and needed a way to convert a string to int and decided to use stringstream(my first time using it). Is there a way to somehow use the same declared stringstram(is that how you call it?) instead of creating new ones for each conversion? I tried using the .clear() function and it still didn't work.
How I did it:
stringstream s0(hour); // this is my way of converting string to int because stoi doesn't seem to work
s0 >> i_hour;
cout << i_hour << endl;
stringstream s1(minute);
s1 >> i_minute;
stringstream s2(second);
s2 >> i_second;`
and how I wanted to do it:
stringstream ss(hour);
ss >> i_hour;
ss.clear();
ss << minute;
ss >> i_minute;
is there any way to do it similarly? Looks really messy to keep declaring new ones.

You can call the str(s) method to initialize an std::istringstream to a new string. You should use std::istringstream if all you're doing is converting from a string.
If the previous conversion resulted in an error you will also need clear(), to clear its error state.
So your example would be:
istringstream ss(hour);
ss >> i_hour;
ss.clear();
ss.str(minute);
ss >> i_minute;

Use
ss.str("");
to clear the stream. ss.clear() resets only the flags.

Related

C++ Stringstream only picking up first string

I have a text file with a series two strings delimited by a colon on each line.
I'm using getline to grab the entire line then string stream to split the two strings and put them onto a vector. The code works fine on the first pass it grabs the strings perfectly. Then after that on the 2nd pass of the while loop and so forth it doesn't grab the new input. The string stream seems to leave the original first values for some reason.
if (infile.is_open()) {
std::stringstream ss;
std::string current_line;
std::string tempProxy;
std::string tempPort;
while (std::getline(infile, current_line)) {
ss << current_line;
std::getline(ss, tempProxy, ':');
std::getline(ss, tempPort);
std::cout << tempProxy << " and " << tempPort << std::endl;
}
Any idea why it doesn't want to grab the strings from current_line on any pass except the first iteration?
You're reusing ss but not resetting it correctly. When you extract the second word from the first line, the stream is exhausted and put in an 'EOF' state. When streams are in this or any other 'error' state they don't do anything. You have to clear the error before you can continue to use them.
If you were to check for errors returned by operator<< and getline in the loop (or if you were to cause ss to throw exceptions on errors*) you would find they are indicating that they are not successful past the first iteration. It's a good general practice to always check for errors, and especially so when you're debugging.
You can clear the error by changing your loop:
while (std::getline(infile, current_line)) {
ss.clear(); // clears the error, not the contents
ss << current_line;
However doing this means that ss will accumulate all the lines in its internal buffer. The code will produce your expected output unless the file is large and you run out of memory or something like that.
You can see the accumulating internal buffer with the following:
while (std::getline(infile, current_line)) {
ss.clear();
ss << current_line;
std::cout << "ss internal buffer: " << ss.str();
Instead of using the formatted input to add ss you are probably better off using the .str() member to set it, which will replace the previous data instead of adding to it.
while (std::getline(infile, current_line)) {
ss.clear();
ss.str(current_line);
Alternatively you can construct a new stringstream in each iteration of the loop. This does ensure that no error states or data are carried over from previous iterations. It may also be slower, but you'll have to profile that for yourself.
while (std::getline(infile, current_line)) {
std::stringstream ss(current_line);
* Exceptions are nice because you don't need to remember to check them... except in cases like this where they're not enabled by default. Also I've noticed some C++ implementations have bugs in their iostreams exception code because people don't use it much.
I think you're looking for something like:
if (infile.is_open()) {
std::stringstream ss;
std::string current_line;
std::string tempProxy;
std::string tempPort;
while (std::getline(infile, current_line)) {
std::stringstream to_split;
to_split.str(current_line);
std::getline(to_split, tempProxy, ':');
std::getline(to_split, tempPort);
std::cout << tempProxy << " and " << tempPort << std::endl;
}

Simple casting conversion on C++

I'm doing an exercise for the college and I have to compare a string added including the header <string>, and a character.
I have a text file with a few lines of data from a census, like
Alabama AL 4849377 Alaska AK 736732 Arizona AZ 6731484
I want to read the state name of each line with a string variable, but the comparison is the only thing that I am asking for, because is where I have the error.
I have this fragment of code:
struct Census{
string name;
int population, code;
};
struct States{
Census state;
};
typedef States Vector[US_STATES];
void loadCensus(ifstream & census, Vector stats){
int i=0;
string readData;
string line;
while (getline(census, line)) {
stringstream linestream(line);
while (linestream >> readData) {
if (linestream >> stats[i].state.name >>
stats[i].state.code >>
stats[i].state.population)
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
i++;
}
}
}
How I should convert readData to an integer to assign stats[i].state.population=readData?
I get an error in line 17 in the linestream >> readData.
You want to use the getline() function instead.
I think ita a member function of ifstream or either compare the not readData to a string ("\n") - double quotation. Or put the read data into a string and check if the sting contains a '\n'.
census >> readData will read the next word (any group of non-whitespace characters) from the input. In order to do this, it will discard all whitespace on its hunt for the next word. '\n' is whitespace, so you will never read it with the >> operator without playing games you probably don't want to play.
Instead of >>, use std::getline to read a line and then use a std::stringstream to break the line up into words.
std::string line;
while (std::getline(census, line)) {
std::stringgstream linestream(line);
while (linestream >> readData) {
statistics.state[i]=readData;
i++;
}
}
But...
I do not believe statistics.state[i]=readData; does quite what you want to do. You probably want something more like:
std::string line;
while (std::getline(census, line)) {
std::stringstream linestream(line);
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
i++;
}
In this state becomes an array or vector of objects that probably looks something like
struct statestats
{
std::string name;
std::string abbreviation;
int population;
};
Breaking it down line by line
std::stringstream linestream(line);
Makes a stringstream. A string stream is a stream like cin and cout or a fstream, but it contains a string. The main use is to buffer and build strings with the same syntax you would use on another stream. In this case we are use it to split up the line into words.
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Needs to be handled in a few parts in a few parts. Over all it is an abbreviation of
if (linestream >> statistics.state[i].name &&
linestream >> statistics.state[i].abbreviation &&
linestream >> statistics.state[i].population)
Each stage of which reads from the linestream into a variable.
Next, the >> operator returns the stream being read, and this is used two ways in the example. The first allows chaining. The output of one >> is used as the input of the next, so if you look at >> as you would a function (and it is a function. See Stream extraction and insertion for more) you can think about it looking something like this:
linestream.read(statistics.state[i].name).read(statistics.state[i].abbreviation).read(statistics.state[i].population)
The >> syntax just makes it easier.
The next advantage you get from returning the stream is the stream can be tested to see if the stream is still good. It has a boolean operator that will return true if the stream is in a good state and can be used.
if(linestream)
{
good
}
else
{
bad
}
will enter good if the stream is open, has not reached the end of the stream, and has had no troubles reading or writing data.
Going back to our example
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Will enter the body of the if statement if the stream successfully read all three values from the stream. Which is not what we want. Ooops. I've corrected the above code already.
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
will enter the body of the if if at least one value was not read for any reason and print out an error message. Normally when there is an error you will need to clear the error before continuing, but in this case we've use the whole stream and are about to discard it.
Assuming no error occurred all of the data from this line has been read and there is no need to
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;

White space insertion in std::stringstream fails

I'm trying to insert white space in std::stringstream in this way.
std::stringstream sstr;
sstr.str("");
sstr << " ";
sstr << 10;
and then setting it as a label like that
label->setString(sstr.str().c_str());
but it's only giving me 10, space is not included. I've followed many links to solve problem but of no use. Following link suggests to use getline() but in my case I cannot do that :
stringstream doesn't accept white space?
I've also tried to use std::noskipws but it also not work :
sstr << std::noskipws << " ";
Any help will be appreciated.
std::stringstream should not remove your whitespace when used like this. Are you sure that it is not the label-object that is trimming your string and removing the whitespace?
Try debugging or printing out your string before setting it to the label.
You can use put method to append a single char:
std::stringstream sstr;
sstr.str("");
sstr.put(' ');
sstr.put(10);
I think you're using it wrong:
string labelstr;
std::stringstream sstr;
sstr.str("");
sstr << " ";
sstr << 10;
ss >> noskipws >> labelstr;
label->setString(labelstr);
http://www.cplusplus.com/reference/ios/noskipws/
Your code works as expected for me. Here's a runnable example: http://cpp.sh/8d6.
The culprit must be setString trimming your input. Or possibly some other function that reads the string later.

get everything from stringstream

I have placed som information to stringstream ss:
stringstream ss (stringstream::in | stringstream::out);
ss<<"abc 456 ";
ss<<123
Then I decided to retrieve string content to string g:
std::string s;
std::string g;
for (int n=0; n<c; n++)
{
ss >> s;
g=g+s;
}
cout << g <<endl;
For this reason I need to know how many placements was done to ss. How to know that? Probably method that retrieves string information is not very clever - then give your way.
I need to know how many placements was done to ss. How to know that?
You'll need to count this yourself, stringstream provides no ability to count how many insertions were done to it.
Probably method that retrieves string information is not very clever - then give your way.
How about:
ss.str()
To get the full string with everything thats been inserted into it.

boost lexical_cast throws exception

I'm using boost libs for c++ and the function lexical_cast behaves really weird. If I do lexical_cast("0.07513994") it works fine, but if I use my variable which I need to convert, it throws the bad_lexical_cast exception. Here is the code:
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
double x;
x = lexical_cast<double>(word);
cout << x << endl;
} while (iss);
What am I doing wrong here? I appreciate any help, thanks
Your problem is probably that the loop is processed one more time than you expect.
The last time through the loop, the read to word fails, setting the fail bit in iss, which is what while(iss) is checking. To fix it you need to do something like this.
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
if(iss)
{
double x;
x = lexical_cast<double>(word);
cout << x << endl;
}
} while (iss);
Unlike functions such as atof() which stop parsing as soon as they see an invalid character, lexical_cast requires that every character in the input string be valid. i.e. any leading or trailing spaces will cause it to throw an exception.
You want to see what kind of input it is getting and trim it accordingly. You should also catch bad_lexical_cast just in case it does get input which is completely garbage.
One possible solution is to use boos.regex or boost.xpressive to extract a valid sub-string and pass the result to lexical_cast.
The problem is probably that you are sending an empty string when there is no data left.
You should change the loop you are using.
Use the while {} loop (not the 'do while' loop). This allows you to read from the stream and test it in a single easy to read statement. Note the result of iss >> word is the stream. When used in this boolean context it is tested to see if the state is good and its value converted into something that can be used by the while condition. Thus if the operator >> filed to work correctly then the loop is never entered.
istringstream iss(line);
string word;
while(iss >> word)
{
double x = lexical_cast<double>(word);
cout << x << endl;
}
But really you don't even need the lexical cast in this situation (unless you want to test for non numbers with an exception). The standard stream operator will convert the input into a double.
istringstream iss(line);
double word;
while(iss >> word)
{
cout << word << endl;
}
if (iss.fail())
{ /* Failure to convert input to a double */
}