Home > Software engineering >  In C and range-v3, how to convert a string of space-separated numbers to a vector of integers?
In C and range-v3, how to convert a string of space-separated numbers to a vector of integers?

Time:02-06

Using C and range-v3 library, what's the optimal approach to converting a string with space-separated numbers to a vector of integers?

I tried the following code:

#include <iostream>
#include <range/v3/all.hpp>

using namespace std::literals;

int main() {
    auto r = "1 1 2 3 5 8 13"sv
        | ranges::views::split(" "sv)
        | ranges::views::transform([](auto &&i){ return std::stoi(std::string{i}); })
        | ranges::to<std::vector<int>>();

    for (auto i: r)
        std::cout << "Value: " << i << std::endl;
}

It doesn't compile however. In clang, the error is as follows:

repro-range.cpp:10:60: error: no matching constructor for initialization of 'std::string' (aka 'basic_string<char>')
                | ranges::view::transform([](auto &&i){ return std::stoi(std::string{i}); })
                                                                         ^          ~~~

It seems that the type of i is ranges::detail::split_outer_iterator and it's not convertible to string. Actually, I don't understand how to use i, can't dereference it, can't convert it to anything useful... replacing string_views by strings also doesn't improve the situation.

What's weird, the code below works fine:

    auto r = "1 1 2 3 5 8 13"sv
        | ranges::views::split(" "sv)
        | ranges::to<std::vector<std::string>>();

which suggest me the problem is netiher split nor to, but the transform itself.

How to make the first piece code working?

CodePudding user response:

Digging deeper, I found out that i in my example isn't an iterator nor a wrapper over string_view (like I expected) but a range of characters (a special type with begin and end iterators).

Meaning, my code works if I first convert i to a string the range way:

    auto r = "1 1 2 3 5 8 13"sv
        | ranges::views::split(" "sv)
        | ranges::views::transform([](auto &&i){
            return std::stoi(i | ranges::to<std::string>());
        })
        | ranges::to<std::vector<int>>();

Although I'll be delighted if somebody posts a nicer (at least less verbose) way to do that.

CodePudding user response:

If you have a string containing space separated numbers you can first create an std::istringstream over the string and then use ranges::istream to parse the numbers (assuming ints here):

auto s = "1 1 2 3 5 8 13";
auto ss = std::istringstream{s};
auto r = ranges::istream<int>(ss)
       | ranges::to<std::vector<int>>();

Here's a demo.

  • Related