Reputation: 183
I've recently taken up C++ and I'm having a bit of difficulty adapting from other languages. I'm sure there's a much easier solution to this, but I can't figure out why this won't work.
Basically, I need a way to turn an input, like 5 66 193
(unknown length of integers), into a vector/something else, like "[5, 66, 193]
".
This is my code:
vector<int> A;
string AToParse;
getline(cin, AToParse);
int pos = 0;
while (pos < AToParse.length()) {
if (AToParse[pos] == ' ') {
pos++;
continue;
}
string tmp = "" + AToParse[pos];
if (pos + 1 >= AToParse.length()) break;
for (int i = pos + 1; i < AToParse.length(); i++) {
if (AToParse[i] == ' ') {
pos = i + 1;
break;
} else {
tmp += AToParse[i];
}
}
int tmpint = stoi(tmp);
A.push_back(tmpint);
}
for (int Ai : A) {
cout << Ai << endl;
}
Upvotes: 0
Views: 72
Reputation: 22162
Requires #include
s for <sstream>
and <iterator>
:
std::stringstream parseStream{AToParse};
std::vector<int> A(std::istream_iterator<int>{parseStream}, std::istream_iterator<int>{});
std::stringstream
behaves the same way as std::cin
or std::ifstream
, only that it uses the std::string
passed to it as input buffer. So you can use int x; parseStream >> x;
and the like as well.
The std::istream_iterator<int>
is a convenient iterator that keeps reading int
s from a stream discarding whitespaces. (the same behavior as that of parseStream >> x;
iterated)
And the std::vector
constructor taking two argument considers the first argument an iterator to the beginning of a source range to construct the vector from and the second the end of that range.
A default-constructed std::istream_iterator<int>
represents the end or failure of the stream input operation.
Putting this all together, the vector will be constructed by reading integer by integer from the AToParse
, discarding whitespaces, until the end of the string is reached or an input failure happens (e.g. because the string contains characters not interpretable as integers).
To make the code more concise you can leave out the type for the second iterator. The iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced be the same as the type of the first argument due to the std::vector
constructor requiring that:
std::stringstream parseStream{AToParse};
std::vector<int> A(std::istream_iterator<int>{parseStream}, {});
Since C++17, you can drop the extra repetition of the type name for the std::vector
. It will be deduced correctly from the iterators due to this new feature in C++17 called class template argument deduction (CTAD) and you will have to mention the type that you want to read from the string only once, in accordance with the dont repeat yourself rule:
std::stringstream parseStream{AToParse};
std::vector A(std::istream_iterator<int>{parseStream}, {});
Note however that this will do the expected thing only when you use parentheses for the std::vector
initializer. It will not do the correct thing with a brace-enclosed initializer list.
Upvotes: 6