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

да еще раз, потому что я недавно задал очень похожий вопрос (как читать список целых чисел, разделенных запятыми), но на этот раз я застрял на чтении строк строк, состоящих из данных, разделенных запятыми. Конечно, должно быть тривиально преобразовать мой предыдущий код, который обрабатывал целые числа, вместо обработки строк фрагментов данных, верно?

Итак, я читаю данные из файла или стандартного ввода, в котором много строк, содержащих слова, разделенные запятыми, например:

hello,this,is,firstrow,sdf763  
this,is,2nd,row  
and,so,on314  

Итак, моя идея состоит в том, чтобы просто читать строки данных из istream, используя ranges::getlines (или ranges::istream_view), передавать каждую строку адаптеру разделенного представления, разделяя запятые, чтобы получить слова (как диапазон диапазонов , к которому я затем присоединяюсь) и, наконец, преобразую/декодирую каждое слово, которое затем помещается в вектор. ИМХО, это должно быть супер просто, как:

std::string decode(const std::string& word);

int main()
{
    using namespace ranges;
    auto lines = getlines(std::cin);           // ["hello,this,is,firstrow,sdf763" "this,is,2nd,row" "and,so,on314" ...]
    auto words = lines | view::split(",");     // [["hello" "this" "is" "firstrow" "sdf763"] ["this" "is" "2nd" "row"] [...]]
    auto words_flattened = words | view::join; // ["hello" "this" "is" "firstrow" "sdf763" "this" "is" "2nd" "row" ...]
    auto decoded_words = words_flattened | view::transform([](const auto& word){
        return decode(word);
    }) | to_vector;

    for (auto word : decoded_words) {
        std::cout << word << "\n";
    }
    std::cout << std::endl;
}

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

Ссылка на мой предыдущий SO-вопрос: Использование диапазона -v3 читать список чисел, разделенных запятыми


person bamse    schedule 28.12.2019    source источник


Ответы (1)


Адаптер разделенного представления, кажется, вообще не разделяет строки, потому что вся строка передается в качестве аргумента для преобразования - почему это так ??

Потому что это именно то, о чем вы случайно просите.

split — это адаптер, который принимает диапазон T и дает диапазон диапазона T, разделенный разделителем, который представляет собой либо один T, либо сам диапазон Ts.

Когда вы пишете:

lines | views::split(",");

lines — это диапазон строк (а не одна строка), и вы просите разделить этот диапазон строк на строку, состоящую из одной запятой. Что бы это сделало, так это то, что если бы у вас был диапазон строк, таких как ["A", ",", "B", "C", "D", ",", "E"] (то есть 7 строк, из которых 2-я и 6-я запятые), вы бы вернули [["A"], ["B", "C", "D"], ["E"]].

Но это не то, что вы хотите.

Вам нужно разделить каждую строку на запятую. Это:

lines | views::transform([](auto const& s) { return s | views::split(','); })

Это берет ваше RangeOf<string> и превращает его в RangeOf<RangeOf<RangeOf<char>>> (это добавляет только один слой range-ности... поскольку string - это RangeOf<char>. Но мы теряем string-ность).

Затем вы можете join их вместе:

lines | views::transform([](auto const& s) { return s | views::split(','); })
      | views::join;

И теперь мы вернулись к RangeOf<RangeOf<char>>. Если то, что мы действительно хотим, это RangeOf<string>, нам нужно собрать каждый элемент обратно в один:

lines | views::transform([](auto const& s) { return s | views::split(','); })
      | views::join
      | views::transform([](auto const& rc) { return rc | to<std::string>; });

В качестве альтернативы вы можете переместить второе преобразование внутрь первого, чтобы собрать strings перед join.

person Barry    schedule 29.12.2019
comment
Это действительно было бы странно специфичным, если бы views::split была операцией, зависящей от строки. - person Davis Herring; 29.12.2019
comment
›Когда вы пишете: ›lines | views::split(","); ›lines представляет собой диапазон строк (а не одну строку), и вы просите ›разделить этот диапазон строк строкой, состоящей из одной запятой. Да, это было моим главным заблуждением. ›Что вам нужно, так это разделить каждую строку на запятую. Это: ›lines | views::transform([](auto const& s) { return s | views::split(','); }) Да, теперь, когда вы мне это объяснили, это имеет смысл! :-) Спасибо за отличное объяснение @Barry - person bamse; 29.12.2019