Convert String containing several numbers into integers
Solution 1
I assume you want to read an entire line, and parse that as input. So, first grab the line:
std::string input;
std::getline(std::cin, input);
Now put that in a stringstream
:
std::stringstream stream(input);
and parse
while(1) {
int n;
stream >> n;
if(!stream)
break;
std::cout << "Found integer: " << n << "\n";
}
Remember to include
#include <string>
#include <sstream>
Solution 2
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
#include <iostream>
int main() {
std::string input;
while ( std::getline( std::cin, input ) )
{
std::vector<int> inputs;
std::istringstream in( input );
std::copy( std::istream_iterator<int>( in ), std::istream_iterator<int>(),
std::back_inserter( inputs ) );
// Log process:
std::cout << "Read " << inputs.size() << " integers from string '"
<< input << "'" << std::endl;
std::cout << "\tvalues: ";
std::copy( inputs.begin(), inputs.end(),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
}
}
Solution 3
// get string
std::string input_str;
std::getline( std::cin, input_str );
// convert to a stream
std::stringstream in( input_str );
// convert to vector of ints
std::vector<int> ints;
copy( std::istream_iterator<int, char>(in), std::istream_iterator<int, char>(), back_inserter( ints ) );
Solution 4
Here is how to split your string into strings along the spaces. Then you can process them one-by-one.
Solution 5
Generic solution for unsigned values (dealing with prefix '-' takes an extra bool):
template<typename InIter, typename OutIter>
void ConvertNumbers(InIter begin, InIter end, OutIter out)
{
typename OutIter::value_type accum = 0;
for(; begin != end; ++begin)
{
typename InIter::value_type c = *begin;
if (c==' ') {
*out++ = accum; accum = 0; break;
} else if (c>='0' && c <='9') {
accum *= 10; accum += c-'0';
}
}
*out++ = accum;
// Dealing with the last number is slightly complicated because it
// could be considered wrong for "1 2 " (produces 1 2 0) but that's similar
// to "1 2" which produces 1 0 2. For either case, determine if that worries
// you. If so: Add an extra bool for state, which is set by the first digit,
// reset by space, and tested before doing *out++=accum.
}
GobiasKoffi
Updated on July 09, 2022Comments
-
GobiasKoffi almost 2 years
I realize that this question may have been asked several times in the past, but I am going to continue regardless.
I have a program that is going to get a string of numbers from keyboard input. The numbers will always be in the form "66 33 9" Essentially, every number is separated with a space, and the user input will always contain a different amount of numbers.
I'm aware that using 'sscanf' would work if the amount of numbers in every user-entered string was constant, but this is not the case for me. Also, because I'm new to C++, I'd prefer dealing with 'string' variables rather than arrays of chars.
-
sbi over 14 years+1 as long as you keep the
std::
prefixes. Their readability is subjective (I, being used to them, find it much better to read), but the enhanced clarity through fully qualified names is objective. -
nothrow over 14 years+1 after replacing with 'using namespace'. Then you can replace implementation with some other, and dont need to cope with namespace changing.
-
Ankit Roy over 14 yearsIf you fear namespace pollution, enclose the code block with braces and use "using namespace std;" inside them. Best of both worlds?
-
sbi over 14 years@Yossarian: I'd like to see the library that comes with
getline
,cin
,vector
,istringstream
,copy
,istream_iterator
,back_inserter
,endl
,cout
, andostream_iterator
-- all with the same signature, with similar, but slightly different semantics, so that replacingstd
with some other lib prefix actually gains something. -
sbi over 14 years@j_random_hacker: I don't actually fear namespace pollution. However, having each name fully qualified makes it easier to see where it comes from. ("Oh, this is a proprietary
vector
template!") Many years ago, in a project I then worked on, we decided to fully qualify all names. It seemed hilarious back then, but became normal within maybe two weeks. I've been on both side of the argument and would never go back. Readability is a subjective question alterable by usage. Clarity is objective. -
Ankit Roy over 14 yearsInteresting perspective sbi. I've certainly become much more maintenance-centric with time, though I haven't gone so far as to favour this approach... yet. (Half-serious taunt: I would prefer to see "vector<vector<int> >" in a compiler error message, but I suppose you would prefer the more precise "std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > >"? :-P) Also just to clarify, it is abhorrent to "using namespace std;" at file scope in a header, so you must std::-qualify there in any case.
-
David Rodríguez - dribeas over 14 yearsUsually error messages are processed by the compiler and the compiler fully qualifies the types. I am usually explicit with respect of std:: namespace. As a matter of fact, only in a few cases I employ the using directive, in some cases create short aliases. Fully qualifying has the advantage of being easier on the casual reader, names as 'find', 'copy', can be common names for member/free functions. The casual reader will know directly when the symbol is part of STL or not. Then again, I don't have such a great drive not to change it besides having to do more editing to the answer :P
-
Ankit Roy over 14 years+1 since you answer the question as stated ("integers"), but I think this is actually a step in the less-generic direction in some respects, as it will clearly only work for integers and not other types (IOW: the "genericity" of "typename OutIter::value_type accum = 0;" is overstated). How would you handle floating point numbers for example? If you want to write another mini-lexer, don't forget to handle scientific notation and Inf, NaN etc.
-
Ankit Roy over 14 yearsWell I'll +1 since you added the headers, but for the time being I remain firmly in the "std:: everywhere makes my eyes bleed" camp. It's almost <shudder> Perlish to look at. :-P
-
MSalters over 14 yearsWell, it will accept
short[]
,std::vector<int>
andstd::list<long>
or in fact any other integral type (including implementation-defined types). Plain 0 will convert to all of those. The reason I used the valuetype there is so I won't accidentily overflow anint
when the user passes as__int128[]
and a string with numbers that big. -
sbi over 14 years@j_random_hacker: I would prefer to see my
typedef
namebla_foo_bar_data
istead ofstd::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > >
-- preferably with an option to expand that to the fully qualified identifier but with all the standard template parameters omitted. And as I said: Readability is a subjective thing and I believe that most programmers can get used to either way (and prefer that) within weeks. I used to be in your camp, but learned to like fully qualified names in two weeks. From that perspective, I wouldn't go back. -
Ankit Roy over 14 yearsUsing value_type for accum is absolutely the right way to go, but I guess I don't see why you couldn't just use an istringstream instead of your own handcrafted lexer. Then you could work with any type understood by operator<<() (i.e. improved "typewise" genericity) without sacrificing the "iteratorwise" genericity of your current solution. I also suspect that's more likely to work with wide chars, locales etc.
-
dalle over 14 years@sbi: By fully qualify you surely mean
::std::vector<int>
, right? -
Mooing Duck over 11 yearsYou mean parse:
int n; while(stream >> n) {std::cout << "Found integer: " << n << "\n";}
? Much cleaner -
Will about 7 years"Readed" should be replaced with "Read" :)
-
Caleth almost 6 yearsOr even parse:
for (int n; stream >> n;) {std::cout << "Found integer: " << n << "\n";}
-
Phillip almost 5 yearsOr use direct initialization:
std::vector<int> ints{std::istream_iterator<int, char>{in}, std::istream_iterator<int, char>{}};