2

If I have the string "2.5 4.2 6.9 1.1", how can I convert that into a vector of doubles as efficiently as possible?

X_Trust
  • 657
  • 1
  • 6
  • 25

3 Answers3

4
vector<double> convert_string_to_vec(std::string const & str)
{
    std::istringstream input{str};
    vector<double> output{std::istream_iterator<double>{input},
                          std::istream_iterator<double>{}};

    return output;
}

You'll need to include <sstream> as well as <iterator> for this to work. Here's a working example.

AliciaBytes
  • 7,300
  • 6
  • 36
  • 47
  • 2
    You need to either use universal initialization, `{}`, or stuff one of those iterators (ideally the first) in a nested set of parens, or you code will hit an [MVP](http://en.wikipedia.org/wiki/Most_vexing_parse). And minor: your decl is missing a semi-colon. – WhozCraig Nov 13 '13 at 00:48
  • Got to hate that mvp... Changed them all to universal initialization and added link to coliru example. thanks craig =) – AliciaBytes Nov 13 '13 at 00:54
  • Be happy we have UI now. Before with all the parens I kept having Lisp flashbacks from college. – WhozCraig Nov 13 '13 at 00:55
  • 1
    You can always use named iterators, which you can declare in the same statement, avoiding mvp *and* saving you from having to tediously type `std::istream_iterator` twice. http://coliru.stacked-crooked.com/a/dca982f0429e3595 – Benjamin Lindley Nov 13 '13 at 01:18
  • This creates a vector and then returns a copy of that vector, so it could be made more efficient. – snips-n-snails Nov 13 '13 at 01:22
  • 1
    @traal: In a world with [RVO](http://en.wikipedia.org/wiki/Return_value_optimization) and [move semantics](http://en.wikipedia.org/wiki/Move_semantics#Rvalue_references_and_move_constructors), no, it really can't. At least, not in that respect. There may be optimization opportunities in the string-to-double conversion though. – Benjamin Lindley Nov 13 '13 at 01:24
  • @traal like Benjamin said already, there shouldn't be a copy at all. And moving is insignificant compared to the `stringstream` performance. – AliciaBytes Nov 13 '13 at 01:32
0

This is how I would generally do it. Possibly not the most efficient way, but very simple.

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

int main()
{
    std::string d("2.5 4.2 6.9 1.1");
    std::stringstream s(d);
    std::vector<double> result;
    double temp;
    while(s >> temp)
    {
        result.push_back(temp);
    }
    for(size_t i = 0; i < result.size(); ++i)
    {
        std::cout << result[i] << "\n";
    }

    return 0;
}
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35
  • is there a specific reason for not using `std::endl` but rather using `"\n"` ? – Varaquilex Nov 13 '13 at 00:52
  • 1
    @Volkanİlbeyli `std::endl` flushes the stream as well as inserting a new line. There's no need to flush after each iteration so `'\n'` is preferred in this case. – David G Nov 13 '13 at 00:53
  • Yeah, the `"\n"` is to avoid extra flushing of the stream. Admittedly not all that important in a small example like this, but I'm trying to teach myself to prefer it for when there is a difference. – Retired Ninja Nov 13 '13 at 00:58
0

Here's a unique way:

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

template <class Facet>
struct erasable_facet : Facet
{
    erasable_facet() : Facet(0) { }
    ~erasable_facet() { }
};

std::vector<double> convert(const std::string& str)
{
    using num_get = std::num_get<char>;

    erasable_facet<num_get> facet;

    std::stringbuf buf(str);
    std::vector<double> v;

    std::ios ios(nullptr);
    std::ios_base::iostate err = std::ios_base::goodbit;

    double d;
    std::istreambuf_iterator<char> it, end;

    do
    {
        it = facet.get(&buf, end, ios, err, d);
        buf.sbumpc(); // skip space

        if (!(err & std::ios_base::failbit) &&
            !(err & std::ios_base::badbit))
            v.push_back(d);
        else
            return v;
    } while (it != end);

    return v;
}

int main()
{
    std::string str = "1.24 5.32 9.53";

    auto v = convert(str);
}
David G
  • 94,763
  • 41
  • 167
  • 253