C++: Reading from stringstream - c++

I have an istringstream object with string of format
STRING,INT,INT,INT
eg.
"name,20,30,40"
I want to read the values into variables of specific types such as std:string and int.
How can I do that?

The lazy way:
getline(stream, str, ',');
char c;
stream >> i1 >> c >> i2 >> c >> i3;
It is "lazy" because it does not handle format errors in any sensible way.
The smarter ways would be split on commas into a vector of strings (which can then be converted into integers as needed), or use a full-fledged parser, such as boost.spirit.

Related

C++ String stream ignore() not working

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));
}

Clear and reuse a stringstream for multiple conversions

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.

Count of a data type in a file in C++

I have a file with strings and integers. For example, this is how my file is
string int int string int int
How do I get the count of only strings in the file? I thought of writing the count in the beginning of the file. But the count was not proper while reading.
If it is a text file, you can easily read the "words" from the file and then determine if they are numbers or not using atoi, atol, atoll, atoq. And when you got an answer (ie: this is a number, or not), just increment a variable :)
Parse through each item. For each item compare the ascii values of the characters in it.If it is between 48 and 57(inclusive) , it is an int. Otherwise it is a string.
std::string word;
while (std::cin >> word)
{
std::istringstream iss(word);
int my_int;
char garbage;
num_ints += (iss >> my_int) && !(iss >> garbage);
++num_words;
}
num_strings = num_words - num_ints;

c++ parsing lines in file as streams

I want to parse a file which describes a set of data line by line. Each datum consists of 3 or four parameters: int int float (optional) string.
I opened file as ifstream inFile and used it in a while loop
while (inFile) {
string line;
getline(inFile,line);
istringstream iss(line);
char strInput[256];
iss >> strInput;
int i = atoi(strInput);
iss >> strInput;
int j = atoi(strInput);
iss >> strInput;
float k = atoi(strInput);
iss >> strInput;
cout << i << j << k << strInput << endl;*/
}
The problem is that the last parameter is optional, so I'll probably run into errors when it is not present. How can i check in advance how many parameters are given for each datum?
Furthermore,
string line;
getline(inFile,line);
istringstream iss(line);
seems a bit reduldant, how could I simplyfiy it?
Use the idiomatic approach in this situation, and it becomes much simpler:
for (std::string line; getline(inFile, line); ) {
std::istringstream iss(line);
int i;
int j;
float k;
if (!(iss >> i >> j)) {
//Failed to extract the required elements
//This is an error
}
if (!(iss >> k)) {
//Failed to extract the optional element
//This is not an error -- you just don't have a third parameter
}
}
By the way, atoi has some highly undesired ambiguity unless 0 is not a possible value for the string you're parsing. Since atoi returns 0 when it errors, you cannot know if a return value of 0 is a successful parsing of a string with a value of 0, or if it's an error unless you do some rather laborious checking on the original string you had it parse.
Try to stick with streams, but in situations where you do need to fall back to atoi type functionality, go with the strtoX family of functions (strtoi, strtol, strtof, etc). Or, better yet, if you're using C++11, use the stoX family of functions.
You could use a string tokenizer How do I tokenize a string in C++?
In particular: https://stackoverflow.com/a/55680/2436175
Side note: you do not need to use atoi, you could simply do:
int i,j;
iss >> i >> j;
(but this wouldn't handle alone the problem of optional elements)

Extract arbitrary data values from a std::string C++

I have strings like this
10z45
9999i4a
Basically int-char-int-optionalchar
I want to do this function prototype
void process(std::string input, int &first, char &c, int &last, bool &optional)
Only thing is I'm not sure the best way to iterate over the string to extract these values. Would rather not use regex library, seems like can be done simply?
Use a string stream:
#include <sstream>
...
std::istringstream iss(input);
iss >> first >> c >> last >> optional;
If there's no final character, the value of optional won't be touched, so I'd recommend setting it to 0 beforehand.
Use std::istringstream, read int, char, int, then try next char:
std::istringstream is(input);
is >> first >> c >> last;
char c2;
optional = (is >> c2);
I'm not sure this is 100% what you want -but I'd do it in this way.