Использование range-v3 для чтения списка чисел, разделенных запятыми

Я бы хотел использовать Ranges (я использую реализацию range-v3) для чтения входного потока, который представляет собой список чисел, разделенных запятыми. Без диапазонов обойтись тривиально, но... Я подумал, что это простой способ решить эту проблему:

auto input = std::istringstream("42,314,11,0,14,-5,37");
auto ints = ranges::istream_view<int>(input) | ranges::view::split(",");
for (int i : ints)
{
    std::cout << i << std::endl;
}

Но это не компилируется. Я пробовал несколько вариантов этого, но, похоже, ничего не работает, я думаю, что это неправильно в нескольких отношениях. Может ли кто-нибудь объяснить мне, что я делаю неправильно, и объяснить, как это сделать?

Заранее спасибо!

2 ответа

Решение

Какой

ranges::istream_view<int>(input)

действительно создает диапазон, который является грубым эквивалентом этой сопрограммы (даже если вы не понимаете сопрограммы C++20, надеюсь, этот пример достаточно прост, чтобы понять суть):

generator<int> istream_view_ints(istream& input) {
    int i;
    while (input >> i) {  // while we can still stream int's out
       co_yield i;        // ... yield the next int
    }
}

Здесь два важных момента:

  1. Это диапазон ints, поэтому вы не можете split это на веревочке.
  2. Это использует обычный поток >>, что не позволяет указывать собственный разделитель - он останавливается только на пробеле.

В целом, istream_view<int>(input) дает вам ряд ints, который, по вашему мнению, состоит из одного int: просто 42. Следующий ввод будет пытаться прочитать в, и потерпеть неудачу.


Чтобы получить ввод с разделителями, вы можете использовать getlines. Это даст вам рядstringс указанным вами разделителем. Оно использует std::getline внутренне. По сути, это сопрограмма:

generator<string> getlines(istream& input, char delim = '\n') {
    string s;
    while (std::getline(input, s, delim)) {
        co_yield s;
    }
}

А затем вам нужно преобразовать эти stringс к intс. Что-то вроде этого должно помочь:

auto ints = ranges::getlines(input, ',')
          | ranges::view::transform([](std::string const& s){ return std::stoi(s); });
std::string input = "42,314,11,0,14,-5,37";
auto split_view = ranges::view::split(input, ",");

даст ряд диапазонов:

{{'4', '2'}, {'3', '1', '4'}, {'1', '1'}, {'0'}, {'1', '4'}, {'-', '5'}, {'3', '7'}}.

так что вы можете сделать:

std::string input = "42,314,11,0,14,-5,37";
auto split_view = ranges::view::split(input, ",");
for (auto chars : split_view) {
    for (auto c : chars) {
        std::cout << c;
    }
    std::cout << std::endl;
}
Другие вопросы по тегам