使用 range-v3 读取以逗号分隔的数据行

Using range-v3 to read lines with comma-separated data

是的,因为我最近问了一个非常相似的问题(如何读取以逗号分隔的整数列表),但这次我坚持阅读由逗号分隔数据组成的字符串行。将我以前处理整数的代码转换为处理数据块的字符串肯定是微不足道的,对吧?

好的,所以我从一个文件或标准输入中读取数据,该文件或标准输入有很多行包含以逗号分隔的单词,例如:

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

所以,我的想法是简单地使用 ranges::getlines(或 ranges::istream_view)从 istream 中读取数据行,将每一行通过管道传输到以逗号分隔的拆分视图适配器,以获得单词(作为一系列范围,然后我将其加入),最后 transform/decode 然后将每个单词放入向量中。恕我直言,它应该非常简单,就像:

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;
}

但是不,这不起作用,我不明白为什么!拆分视图适配器似乎根本不拆分行,因为整行都作为参数传递给 transform - 为什么会这样?? 我显然仍在学习射程,但似乎仍然错过了一些基本概念……如果有人能解释发生了什么,我将不胜感激,在此先感谢!

我之前的 SO 问题的 link:

The split view adaptor seem to not split the lines at all because the whole line is passed as an argument to transform - why is that??

因为这正是您不小心要求的。

split 是一个适配器,它采用 T 的范围并产生 T 的范围,在分隔符上拆分为单个 T 或者它本身就是 T 的范围。

当你写:

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

lines 是一系列字符串(不是单个字符串),您要求将 字符串范围 拆分为单个逗号的字符串.如果你有一系列字符串,如 ["A", ",", "B", "C", "D", ",", "E"](即 7 个​​字符串,其中第 2 个和第 6 个是逗号),你会得到 [["A"], ["B", "C", "D"], ["E"]]

但这不是你想要的。

你想要的是用逗号拆分 each 字符串。那是:

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

这会将你的 RangeOf<string> 变成 RangeOf<RangeOf<RangeOf<char>>>(这只会增加一层 range-ness... 因为 stringRangeOf<char>. 但我们失去了 string-ness).

然后您可以 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>; });

或者,您可以将第二个变换移到第一个变换内部,以便在 join 之前收集到 string 秒。