I was solving a question on hackerrank and came across this problem involving string streams.
https://www.hackerrank.com/challenges/c-tutorial-stringstream/problem
For Extracting data, hackerrank has given an example:
stringstream ss("23,4,56");
char ch;
int a, b, c;
ss >> a >> ch >> b >> ch >> c; // a = 23, b = 4, c = 56
However, when I try to export it to a vector, I have to escape the ',' using:
stringstream ss(str);
vector<int> vect;
int i;
while (ss >> i)
{
vect.push_back(i);
if (ss.peek() == ',')
ss.ignore();
}
Why can't I use the extraction operation to get the required word here? Shouldn't the stream escape the ','(Sorry for the noob-level question)?
operator>> extracts the next delimited token, only so far as characters actually belong to the requested data type. So, when using operator>> to read an int, it will extract only digits, not letters, punctuation, etc. That means a comma following a number has to be read separately.
In the first example:
ss >> a reads the first int in the stream
then >> ch reads the comma after it
then >> b reads the next int
then >> ch reads the comma after it
then >> c reads the next int
In the second example:
ss >> i reads the next int in the stream, breaking the loop if fails or EOF
then ss.peek() checks if a comma exists (since the last int doesn't have one), and if found then ss.ignore() skips past it
goto #1
If you try to use operator>> to read a comma that doesn't exist, it will set the stream's eofbit state and fail the extraction. If you use while (ss >> i >> ch), the while would evaluate as false when the last int is reached. Even though ss >> i would succeed, >> ch would fail, and thus i would not be added to the vector.
In theory, you could replace if (ss.peek() == ',') ss.ignore(); inside the loop with char ch; ss >> ch instead. The end effect would be the same, at least for a string like "23,4,56". But, let's say you were given something like "23 4 56" instead. The first example would fail to handle that correctly, but the second example would handle it just fine when using peek()+ignore(), but not when using ss >> ch.
I think you can use this code to escape the ','
std::string valstr;
while (std::getline(ss, valstr, ','))
{
vect.push_back(std::stoi(valstr));
}
Related
Is there a default delimiter for stringstream? From my research, I understood that I can use it to split a string using space and comma as delimiters. But can I use other delimiters for stringstream?
Here is a C++ code snippet :
vector<int> parseInts(string str) {
// Complete this function
stringstream ss(str);
vector<int> res;
char ch;
int x;
while(ss){
ss >> x >> ch;
res.push_back(x);
}
return res;
}
This code works without me mentioning any specific delimiter. How does that happen?
There is no "delimiter" for streams at all. operator>>, on the other hand, implements its reading by delimiting on whitespace characters. For other delimiter characters, you can use std::getline() instead, eg:
vector<int> parseInts(string str) {
// Complete this function
istringstream iss(str);
vector<int> res;
int x;
string temp;
char delim = '-'; // whatever you want
while (getline(iss, temp, delim)) {
if (istringstream(temp) >> x) { // or std::stoi(), std::strtol(), etc
res.push_back(x);
}
}
return res;
}
This code works without me mentioning any specific delimiter. How does that happen?
streams don't know anything about delimiters. What is happening is that, on each loop iteration, you are calling ss >> x to read the next available non-whitespace substring and convert it to an integer, and then you are calling ss >> ch to read the next available non-whitespace character following that integer. The code doesn't care what that character actually is, as long as it is not whitespace. Your loop runs until it reaches the end of the stream, or encounters a reading/conversion error.
I tried this code which can split the string by comma:
string spa ="12,233,434,2";
istringstream iss(spa + ",");
int val;
char dot =',';
while(iss >> val >> dot){
cout << val << endl;
}
But I have no idea how it works. It seems to extract the value firstly, then right-shifts?
iss >> val >> dot reads an int and a char, which happens to match the format of the input.
Their values prior to reading doesn't matter at all.
The while tests if the read was successful, and stops when the input fails.
How do I get rid of the leading ' ' and '\n' symbols when I'm not sure I'll get a cin, before the getline?
Example:
int a;
char s[1001];
if(rand() == 1){
cin >> a;
}
cin.getline(s);
If I put a cin.ignore() before the getline, I may lose the first symbol of the string, so is my only option to put it after every use of 'cin >>' ? Because that's not very efficient way to do it when you are working on a big project.
Is there a better way than this:
int a;
string s;
if(rand() == 1){
cin >> a;
}
do getline(cin, s); while(s == "");
Like this:
std::string line, maybe_an_int;
if (rand() == 1)
{
if (!(std::getline(std::cin, maybe_an_int))
{
std::exit(EXIT_FAILURE);
}
}
if (!(std::getline(std::cin, line))
{
std::exit(EXIT_FAILURE);
}
int a = std::stoi(maybe_an_int); // this may throw an exception
You can parse the string maybe_an_int in several different ways. You could also use std::strtol, or a string stream (under the same condition as the first if block):
std::istringstream iss(maybe_an_int);
int a;
if (!(iss >> a >> std::ws) || iss.get() != EOF)
{
std::exit(EXIT_FAILURE);
}
You could of course handle parsing errors more gracefully, e.g. by running the entire thing in a loop until the user inputs valid data.
Both the space character and the newline character are classified as whitespace by standard IOStreams. If you are mixing formatted I/O with unformatted I/O and you need to clear the stream of residual whitespace, use the std::ws manipulator:
if (std::getline(std::cin >> std::ws, s) {
}
My understanding is that the operator >> returns the left operand, so cin >> x returns cin which has a value of false if we encounter an EOF or other errors. Now my guess is that since cin skips whitespace, characters like \n don't get stored. What I'd like to do is something like this:
int x;
while((cin >> x) and x != '\n')
// do stuff
So that when cin encounters a newline in the stream, it exits the input loop (as it would normally do with say, EOF).
Using >> operator directly won't work, because as you say it skips whitespace including newlines.
What you can do is to read a single line using std::getline, and then you can read all input from the line with std::stringstream.
std::string line;
if (std::getline(std::cin, line)) {
std::istringstream ss(line);
int x;
while (ss >> x) {
//....
}
}
I am currently doing c++ and am going through how to take in an sentence through a string and reverse the words (This is a word......word a is This etc)
I have looked at this method:
static string reverseWords(string const& instr)
{
istringstream iss(instr);
string outstr;
string word;
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
}
int main()
{
string s;
cout << "Enter sentence: ";
getline(cin, s);
string sret = reverseWords(s);
cout << reverseWords(s) << endl;
return 0;
}
I have gone through the function and kind of understand but I am a bit confused as to EXACTLY what is going on at
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
Can anybody explain to me the exact process that is happening that enables the words to get reversed?
Thank you very much
iss is an istringstream, and istringstreams are istreams.
As an istream, iss has the operator>>, which reads into strings from its string buffer in a whitespace delimeted manner. That is to say, it reads one whitespace separated token at a time.
So, given the string "This is a word", the first thing it would read is "This". The next thing it would read would be "is", then "a", then "word". Then it would fail. If it fails, that puts iss into a state such that, if you test it as a bool, it evaluates as false.
So the while loop will read one word at a time. If the read succeeds, then the body of the loop appends the word to the beginning of outstr. If it fails, the loop ends.
iss is a stream, and the >> is the extraction operator. If you look upon the stream as a continuous line of data, the extraction operator removes some data from this stream.
The while loop keep extracting words from the stream until it is empty (or as long as the stream is good one might say). The inside of the loop is used to add the newly extracted word to the end of the outstr
Look up information about c++ streams to learn more.
The instruction:
istringstream iss(instr);
allows instr to be parsed when the operator>> is used, separating words thourgh a whitespace character. Each time the operator >> is used it makes iss point to the next word of the phrase stored by instr.
iss >> outstr; // gets the very first word of the phrase
while (iss >> word) // loop to get the rest of the words, one by one
{
outstr = word + ' ' + outstr; // and store the most recent word before the previous one, therefore reversing the string!
}
return outstr;
So the first word retrieved in the phrase is actually stored in the last position of the output string. And then all the subsequent words read from the original string will be put before the previous word read.