2
\$\begingroup\$

Sometimes, I need to read two integer parameters in a single input line, separated by a whitespace.

I have these two functions that seem to do the job just fine:

#include <iostream>
#include <sstream>
#include <deque>

// Splits a string by its whitespaces and returns a deque with each item.
deque<string> split(string original) {
    deque<string> result;
    string buffer;
    stringstream stream(original);
    while (stream >> buffer) {
        result.push_back(buffer);
    }
    return result;
}

// Reads two integer parameters from a single line, separated by a whitespace
void getTwiceInput(int &a,int &b) {
    string input;
    getline(cin,input);
    deque<string> deck = split(input);
    stringstream ss;
    ss << deck[0];
    ss >> a;
    ss.clear();
    ss << deck[1];
    ss >> b;
}

So in my program I do this call:

int x,y;
getTwiceInput(x, y);

Is there an easier way to achieve this? It is a bit tiring having to type all of that just to achieve this for every programming challenge given.

\$\endgroup\$
  • \$\begingroup\$ If you're always reading two values from a line, consider using std::pair instead of std::deque. In fact, why do you even use std::deque? Use just std::vector, it's generally more efficient except cases where you have to insert values in random position, especially in front. \$\endgroup\$ – Archie Apr 18 '13 at 9:03
  • \$\begingroup\$ @Archie: Haha, yeah XD. I'm too used to deque. \$\endgroup\$ – Omega Apr 18 '13 at 9:37
  • 2
    \$\begingroup\$ Why not: std::cin >> x >> y; \$\endgroup\$ – Martin York Apr 19 '13 at 17:16
3
\$\begingroup\$

I would recommend to use std::stringstream for split. Here is how I think this function would look like:

#include <sstream>
#include <vector>
#include <iterator>

template<typename T>
std::vector<T> split(const std::string& line) {
    std::istringstream is(line);
    return std::vector<T>(std::istream_iterator<T>(is), std::istream_iterator<T>());
}

And here is how to use it:

std::string line = "1.2 3.4 5.6e7";
std::vector<double> vec = split<double>(line);

This method is more general and can split more than two elements as well as parse them to any type provided as template parameter, as long as it is “readable” by operator>>.

\$\endgroup\$
  • \$\begingroup\$ Would std::assign be better than std::copy ? \$\endgroup\$ – William Morris Apr 18 '13 at 19:17
3
\$\begingroup\$

I can think of two more ways of doing it.

1. Using iostream iterators. (Following code is from here)

#include <iostream>
#include <iterator>
using namespace std;

int main () {
  double value1, value2;
  cout << "Please, insert two values: ";

  istream_iterator<double> eos;         // end-of-stream iterator
  istream_iterator<double> iit (cin);   // stdin iterator

  if (iit!=eos) value1=*iit;

  iit++;
  if (iit!=eos) value2=*iit;

  cout << value1 << "*" << value2 << "=" << (value1*value2) << endl;

  return 0;
}

2. Converting string to int using atoi() rest everything will be same.

\$\endgroup\$
  • \$\begingroup\$ Streams are nice, but using atoi() is not a good idea. Better use std::stoi() if your compiler handles C++11 Standard. \$\endgroup\$ – Archie Apr 18 '13 at 9:05
3
\$\begingroup\$

Sorry, what is the problem with:

 cin >> x >> y; 

Or:

void getTwiceInput(int &a,int &b) {
    string input;
    getline(cin,input);
    stringstream ss(input);
    ss >> a >> b;
}

?? Here maybe you need to detect format errors (no mummers or only one per line, or more than two..) But your code dont do that anyway.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.