简体   繁体   中英

How can I read a 2-columns CSV file into a map with ranges?

I'm given a CSV file with two elements per line:

1,2
12,40
11,7
...

which I want to read into a std::map<int, int> .

How can I do that, using anything from Ranges library and Range-v3 ?

At the moment this is where I've got (with the help of this answer ):

#include <boost/hof/lift.hpp>
#include <iostream>
#include <range/v3/istream_range.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/istream.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/chunk.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/split.hpp>
#include <string>

using ranges::istream;
using ranges::to;
using namespace ranges::views;

constexpr auto splitAtComma = [](auto const& r) { return r | split(','); };
constexpr auto rngToString = [](auto const& r) { return r | to<std::string>; };
constexpr auto strToInt = BOOST_HOF_LIFT(std::stoi);

constexpr auto parseCoords = transform(splitAtComma)
                           | join
                           | transform(rngToString)
                           | transform(strToInt)
                           | chunk(2);
int main() {

    auto lines = istream<std::string>(std::cin);

    auto coords = lines | parseCoords;

    std::cout << coords << std::endl;
}

which, invoked like this

./main <<END
1,2
12,40
11,7
END

gives this output

[[1,2],[12,40],[11,7]]

The point is that now I don't know how convert that range-of-ranges into a map. Also, I have the feeling that I'm in a dead-end street, because each element of a std::map<int, int> comes from 2 int s, but in the range-of-ranges above [1,2] , [12,40] , and [11,7] are just ranges of int s, not encoding at compile tiime that there's 2 int s only.

There is no need to use join here because r | split(',') r | split(',') already gives you a range with two elements. Prefer std::from_chars over std::stoi .

You can do this only using the standard library's <ranges>

#include <ranges>
#include <charconv>
#include <fmt/ranges.h>
#include <sstream>

auto toInt = [](auto r) {
  int i = 0;
  std::from_chars(std::to_address(r.begin()), std::to_address(r.end()), i);
  return i;
};

auto parseCoords = 
  std::views::transform(
    [](auto r) { return std::move(r) | std::views::split(','); })
| std::views::transform(
    [](auto r) { return std::pair{toInt(r.front()), toInt(*++r.begin())}; });

int main() {
  auto in = std::istringstream{R"(
1,2
12,40
11,7
)"};
  auto coords = std::views::istream<std::string>(in)
              | parseCoords;
  fmt::print("{}\n", coords);
}

Demo

Note that the std::move(r) in views::transform is necessary because we need to construct an owning_view to avoid the dangling issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM