Convert String containing several numbers into integers

82,686

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.
}
Share:
82,686
GobiasKoffi
Author by

GobiasKoffi

Updated on July 09, 2022

Comments

  • GobiasKoffi
    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
    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
    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
    Ankit Roy over 14 years
    If you fear namespace pollution, enclose the code block with braces and use "using namespace std;" inside them. Best of both worlds?
  • sbi
    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, and ostream_iterator -- all with the same signature, with similar, but slightly different semantics, so that replacing std with some other lib prefix actually gains something.
  • sbi
    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 vectortemplate!") 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
    Ankit Roy over 14 years
    Interesting 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
    David Rodríguez - dribeas over 14 years
    Usually 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
    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
    Ankit Roy over 14 years
    Well 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
    MSalters over 14 years
    Well, it will accept short[], std::vector<int> and std::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 an int when the user passes as __int128[] and a string with numbers that big.
  • sbi
    sbi over 14 years
    @j_random_hacker: I would prefer to see my typedef name bla_foo_bar_data istead of std::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
    Ankit Roy over 14 years
    Using 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
    dalle over 14 years
    @sbi: By fully qualify you surely mean ::std::vector<int>, right?
  • Mooing Duck
    Mooing Duck over 11 years
    You mean parse: int n; while(stream >> n) {std::cout << "Found integer: " << n << "\n";}? Much cleaner
  • Will
    Will about 7 years
    "Readed" should be replaced with "Read" :)
  • Caleth
    Caleth almost 6 years
    Or even parse: for (int n; stream >> n;) {std::cout << "Found integer: " << n << "\n";}
  • Phillip
    Phillip almost 5 years
    Or use direct initialization: std::vector<int> ints{std::istream_iterator<int, char>{in}, std::istream_iterator<int, char>{}};