views:

716

answers:

9

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.

A: 
//I modified this example from the link to make it... right
#include <iostream>

int main() {

    int p, q, r;

    std::cout << "Enter 3 integers separated by space: \n";
    std::cin >> p >> q >> r;
    std::cout << "Sum of the " << p << "," << q << 
                 " and " << r << " is = " << (p+q+r) << endl;

    return 0;

}

http://www.tenouk.com/Module18.html

cin>> is capable of doing formatted input.
If you have three ints, p, q, and r,
you just put those in that sequence after cin like in the example.
It'll skip the spaces for you and convert the "numbers" in the string into ints.

Carson Myers
The question says that there aren't always three numbers (and there may be an arbitrary number of them)
Jesse Beder
iostream.h is not part of standard c++
anon
You missed `iostream.h` while making the snippet... right. :)
avakar
oops, didn't read the question carefully enough.
Carson Myers
Carson Myers
A: 

Here is how to split your string into strings along the spaces. Then you can process them one-by-one.

Zed
A: 

try strtoken to separate the string first, the you will deal with each string

Benny
+3  A: 

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>
Jesse Beder
A: 
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
using namespace std;

int ReadNumbers( const string & s, vector <int> & v ) {
    istringstream is( s );
    int n;
    while( is >> n ) {
     v.push_back( n );
    }
    return v.size();
}

int main() {
    string s;
    vector <int> v;
    getline( cin, s );
    ReadNumbers( s, v );
    for ( int i = 0; i < v.size(); i++ ) {
     cout << "number is " <<  v[i] << endl;
    }
}
anon
+2  A: 
#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 << "Readed " << 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;
   }
 }
David Rodríguez - dribeas
+1 once you replace 1000 "std::"s with a single "using namespace std;" and mention the headers you need.
j_random_hacker
+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.
sbi
+1 after replacing with 'using namespace'. Then you can replace implementation with some other, and dont need to cope with namespace changing.
Yossarian
If you fear namespace pollution, enclose the code block with braces and use "using namespace std;" inside them. Best of both worlds?
j_random_hacker
@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
@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.
sbi
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.
j_random_hacker
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
David Rodríguez - dribeas
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
j_random_hacker
@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.
sbi
@sbi: By fully qualify you surely mean `::std::vector<int>`, right?
dalle
A: 
// 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 ) );
Kirill V. Lyadvinsky
A: 

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.
}
MSalters
+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.
j_random_hacker
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.
MSalters
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.
j_random_hacker
+4  A: 
#include <....>
int main()
{
   std::string s = "1 23 456 7890";
   std::deque<int> lst;
   strtk::parse(s," ",lst);
   std::copy(lst.begin(),lst.end(),std::ostream_iterator<int>(std::cout,"\t"));
   return 0;
}
Beh Tou Cheh