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.
Related
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));
}
I'm trying to write a program that reads a bunch of strings from the user, then a newline, and pushes all the strings I've read onto a stack. Here's what I have so far:
stack<string> st;
string str;
while(str != "\n")
{
cin >> str;
st.push(str);
}
However, this goes into an infinite loop and doesn't stop when I read a newline. Why is this happening? How do I fix it?
By default, the stream extraction operator (the >> operator) as applied to strings will skip over all whitespace. If you type in A B C, then a newline, then D E F, then try reading strings one at a time using the stream extraction operator, you'll get the strings "A", "B", "C", "D", "E", and "F" with no whitespace and no newlines.
If you want to read a bunch of strings until you hit a newline, you can consider using std::getline to read a line of text, then use an std::istringstream to tokenize it:
#include <sstream>
/* Read a full line from the user. */
std::string line;
if (!getline(std::cin, line)) {
// Handle an error
}
/* Tokenize it. */
std::istringstream tokenizer(line);
for (std::string token; tokenizer >> token; ) {
// Do something with the string token
}
As a note - in your original code, you have a loop that generally looks like this:
string toRead;
while (allIsGoodFor(toRead)) {
cin >> toRead;
// do something with toRead;
}
This approach, in general, doesn't work because it will continue through the loop one time too many. Specifically, once you read an input that causes the condition to be false, the loop will keep processing what you've read so far. It's probably a better idea to do something like this:
while (cin >> toRead && allIsGoodFor(toRead)) {
do something with toRead;
}
Try doing
stack<string> st;
string str;
while(str!="\n")
{
cin>>str;
if(str == "\n")
{
break;
}
st.push(str);
}
And see if that works.
And if not, then try
while ((str = cin.get()) != '\n')
instead of
while(str!="\n")
Is there way to detect end of line in stringstream?
My file:
1/2
2/3
3/4
4/5
Something like that is not working:
stringstream buffer;
buffer << file.rdbuf();
string str;
getline(buffer, str);
...
istringstream ss(str);
int num;
ss >> num;
if (ss.peek() == '/') //WORKS AS EXPECTED!
{...}
if(ss.peek() == '\n') //NOT WORKING! SKIPS THIS CONDITION.
{...}
This is was warned:
if(ss.telg() == -1) //WARNED!
~~~~~
{...}
std::istringstream has an eof() method:
Returns true if the associated stream has reached end-of-file. Specifically, returns true if eofbit is set in rdstate().
string str;
istringstream ss(str);
int num;
ss >> num;
if (ss.eof()) {...}
You could always use find_first_of:
std::string str_contents = buffer.str();
if(str_contents.find_first_of('\n') != std::string::npos) {
//contains EOL
}
find_first_of('\n') returns the first instance of the EOL character. If there are none, then it returns (a very large index) std::string::npos. If you know that there is a EOL character in your string, you can get the the first line using
std::string str;
std::getline(buffer, str);
Also see NathanOliver's Answer
I have a constraint to read the input strings character by character. So I'm checking for \n after each string. But the program is not terminating.
Here's the problem I'm facing in a very short code:
#include <iostream>
using namespace std;
int main()
{
char c;
while(cin >> c)
{
char x;
cin >> x;
while(x != '\n')
{
// print the characters
cin >> x;
}
}
return 0;
}
In the above code, c will have the first character of the string while x will have the rest of characters one by one.
Input Case:
banananobano
abcdefhgijk
Radaradarada
I have a constraint to read the input strings character by character
One way of reading character by character, is via std::basic_istream::get.
If you define
char c;
then
std::cin.get(c);
will read the next character into c.
In a loop, you could use it as
while(std::cin.get(c))
<body>
cin is whitespace delimited, so any whitespace (including \n) will be discarded. Thus, x will never be
Use getline for reading line from the input stream and then use istringstream to get formatted input from the line.
std::string line;
std::getline(cin, line);
std::istringstream iss(line);
while ( iss >> c) {
print the characters;
}
I have an input stream containing integers and special meaning characters '#'. It looks as follows:
... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ...
The tokens are separated by space. There's no pattern for the position of '#'.
I was trying to tokenize the input stream like this:
int value;
std::ifstream input("data");
if (input.good()) {
string line;
while(getline(data, line) != EOF) {
if (!line.empty()) {
sstream ss(line);
while (ss >> value) {
//process value ...
}
}
}
}
The problem with this code is that the processing stops when the first '#' is encountered.
The only solution I can think of is to extract each individual token into a string (not '#') and use atoi() function to convert the string to an integer. However, it's very inefficient as the majority tokens are integer. Calling atoi() on the tokens introduces big overhead.
Is there a way I can parse the individual token by its type? ie, for integers, parse it as integers while for '#', skip it. Thanks!
One possibility would be to explicitly skip whitespace (ss >> std::ws), and then to use ss.peek() to find out if a # follows. If yes, use ss.get() to read it and continue, otherwise use ss >> value to read the value.
If the positions of # don't matter, you could also remove all '#' from the line before initializing the stringstream with it.
Usually not worth testing against good()
if (input.good()) {
Unless your next operation is generating an error message or exception. If it is not good all further operations will fail anyway.
Don't test against EOF.
while(getline(data, line) != EOF) {
The result of std::getline() is not an integer. It is a reference to the input stream. The input stream is convertible to a bool like object that can be used in bool a context (like while if etc..). So what you want to do:
while(getline(data, line)) {
I am not sure I would read a line. You could just read a word (since the input is space separated). Using the >> operator on string
std::string word;
while(data >> word) { // reads one space separated word
Now you can test the word to see if it is your special character:
if (word[0] == "#")
If not convert the word into a number.
This is what I would do:
// define a class that will read either value from a stream
class MyValue
{
public:
bool isSpec() const {return isSpecial;}
int value() const {return intValue;}
friend std::istream& operator>>(std::istream& stream, MyValue& data)
{
std::string item;
stream >> item;
if (item[0] == '#') {
data.isSpecial = true;
} else
{ data.isSpecial = false;
data.intValue = atoi(&item[0]);
}
return stream;
}
private:
bool isSpecial;
int intValue;
};
// Now your loop becomes:
MyValue val;
while(file >> val)
{
if (val.isSpec()) { /* Special processing */ }
else { /* We have an integer */ }
}
Maybe you can read all values as std::string and then check if it's "#" or not (and if not - convert to int)
int value;
std::ifstream input("data");
if (input.good()) {
string line;
std::sstream ss(std::stringstream::in | std::stringstream::out);
std::sstream ss2(std::stringstream::in | std::stringstream::out);
while(getline(data, line, '#') {
ss << line;
while(getline(ss, line, ' ') {
ss2 << line;
ss2 >> value
//process values ...
ss2.str("");
}
ss.str("");
}
}
In here we first split the line by the token '#' in the first while loop then in the second while loop we split the line by ' '.
Personally, if your separator is always going to be space regardless of what follows, I'd recommend you just take the input as string and parse from there. That way, you can take the string, see if it's a number or a # and whatnot.
I think you should re-examine your premise that "Calling atoi() on the tokens introduces big overhead-"
There is no magic to std::cin >> val. Under the hood, it ends up calling (something very similar to) atoi.
If your tokens are huge, there might be some overhead to creating a std::string but as you say, the vast majority are numbers (and the rest are #'s) so they should mostly be short.