4

I'm wondering what the best way to write from an std::stringstream into a vector<int>.

Here's an example of what's in the stringstream: "31 #00 532 53 803 33 534 23 37"

Here's what I've got:

int buffer = 0;
vector<int> analogueReadings;
stringstream output;

 while(output >> buffer)
     analogueReadings.push_back(buffer);

However what seems to happen is, it reads the first thing, then it gets to #00 and returns 0 because it's not a number.

Ideally, what I want is, it gets to a # and then just skips all characters until the next whitespace. Is this possible with flags or something?

Thanks.

Johan Lundberg
  • 23,281
  • 9
  • 67
  • 91
Jean-Luc
  • 3,254
  • 7
  • 35
  • 71

4 Answers4

5
#include <iostream>
#include <sstream>
#include <vector>

int main ( int, char ** )
{
    std::istringstream reader("31 #00 532 53 803 33 534 23 37");
    std::vector<int> numbers;
    do
    {
        // read as many numbers as possible.
        for (int number; reader >> number;) {
            numbers.push_back(number);
        }
        // consume and discard token from stream.
        if (reader.fail())
        {
            reader.clear();
            std::string token;
            reader >> token;
        }
    }
    while (!reader.eof());

    for (std::size_t i=0; i < numbers.size(); ++i) {
        std::cout << numbers[i] << std::endl;
    }
}
André Caron
  • 41,491
  • 10
  • 58
  • 117
  • If you don't want to read the whole sequence at once, you can remove the outer do-while loop. In this case, you can read series of numbers delimited by non-number tokens. – André Caron Feb 04 '12 at 08:27
  • Run the code. It prints 31, 532, 53, 803, 33, 534, 23, 37. No 0 is inserted. – André Caron Feb 04 '12 at 08:29
  • Merci André. I like your algorithm. I wasn't aware of the fail bits and stuff. Thanks for your time. – Jean-Luc Feb 04 '12 at 10:26
  • +1. This is the way to do it. But only if you are fine with interpreting for example 0x2A as 0 and 55bla as 55. Else see my answer or something along that line. – Johan Lundberg Feb 04 '12 at 10:49
  • Also note that with this method fails catastrophically when converting for example 25 10000000000 77 0 0 due to the uncontrolled overflow. On my system the result is 25 0 0 (note that also 77 is missing). – Johan Lundberg Feb 04 '12 at 12:09
  • Curiously, did you use a DO-WHILE loop as opposed to a WHILE loop, to save on checking output.eof() once? I'm just wondering if that was the only reason. – Jean-Luc Feb 05 '12 at 12:36
  • @user968243: No, it just seemed natural to use a do while loop since the stream bits are set *after* an I/O operation. It seems much clearer to me to try I/O, then check the failure condition, then check the end of file condition. This loops reads like so: "Try to some numbers. If reading a number failed, skip the non-number token. If you did not reach the end of file, try reading numbers again." – André Caron Feb 05 '12 at 18:05
  • Yeah, I guess that seems very logical. Thanks for the explanation. – Jean-Luc Feb 06 '12 at 00:16
1

You need to test if you got a number or not. use the answer from here:

How to determine if a string is a number with C++?

#include <iostream>
#include <sstream>
#include <vector>
using namespace std;

bool is_number(const std::string& s){
   std::string::const_iterator it = s.begin();
   while (it != s.end() && std::isdigit(*it)) ++it;
   return !s.empty() && it == s.end();
}
int main ()
{
    vector<int> analogueReadings;
    std::istringstream output("31 #00 532 04hello 099 53 803 33 534 23 37");

    std::string tmpbuff;
    while(output >> tmpbuff){
      if (is_number(tmpbuff)){
         int num;
         stringstream(tmpbuff)>>num;
         analogueReadings.push_back(num);
       }
    }
}

the result is 31 532 99 53 803 33 534 23 37

Also, important drawbacks of using lexical casts like this is described here: How to parse a string to an int in C++? , where an alternative to tringstream(tmpbuff)>>num is given.

For example 04hello becomes 4 and 7.4e55 becomes 7. There are also terrible problems with underflow and underflow. The clean solution by André Caron converts

25 10000000000 77 0 0

into

25 0 0 

on my system. Note that also 77 is missing!

Community
  • 1
  • 1
Johan Lundberg
  • 23,281
  • 9
  • 67
  • 91
  • The `output >> buffer` expression with `buffer` of type `int` already tests to see if its a number. The trick is that OP's loop condition test is ambiguous: it doesn't distinguish between the end of stream and a failure to read a number. – André Caron Feb 04 '12 at 08:18
  • By the way, this a terrible algorithm. In the common case (the token is a number), the token's characters are processed 3 times: once to read the token into the string, once to perform the "is a number" test, and once to parse the number. – André Caron Feb 04 '12 at 08:33
  • I believe that if the first item in the stringstream is NAN, then the while loop will never run again. I think that's why André Caron used a DO-WHILE loop. – Jean-Luc Feb 04 '12 at 10:24
  • My method works in all cases of base 10 integers, ignoring anything else. Also see the EDIT note. I don't understand what you mean by NAN in this case since we deal with strings and integers, not floats. If the first item is not an integer it is ignored as it should. – Johan Lundberg Feb 04 '12 at 10:55
0

I think the best method would be to

#include <string>
#include <sstream>
#include <vector>

using namespace std;   

int main()
{   
  string numbers = "23,24,27,28";   
  vector<int> integers;   
  stringstream s(numbers);   
  char ch;   
  int a;   
  while(s>>a>>ch) integers.push_back(a);   //s >> reads int char pair 
  s>>a;                                    // reads the last int
  integers.push_back(a); 

  for(int i = 0; i < integers.size(); i++) cout << integers[i] << "\n";
}
J11
  • 395
  • 3
  • 6
0

No loops version:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <sstream>

using namespace std;

class IntegerFiller
{
    vector<int> &m_vec;
public:
    IntegerFiller(vector<int> &vec): m_vec(vec) {}

    void operator()(const std::string &str)
    {
        stringstream ss(str);
        int n;
        ss >> n;
        if ( !ss.fail() )
            m_vec.push_back(n);
    }
};

int main()
{
    vector<int> numbers;
    IntegerFiller filler(numbers);
    for_each(istream_iterator<string>(cin), istream_iterator<string>(), filler);
    copy(numbers.begin(), numbers.end(), ostream_iterator<int>(cout, " "));
    return 0;
}
n0rd
  • 9,674
  • 3
  • 31
  • 50