how to split a string into sequences defined by a rule with range v3 library?

108 Views Asked by At

I have a string of vehicle number, for example kz083y68 and i want to convert it into kz 083 y 68 with spaces. String also could be 3486nv09, etc, so there is no fixed position to split. All i need is to split subsequences of numbers and letters by spaces.

Obviously this can be done manually, but I'm wondering if there is a function in the range-v3 library for this. In general, maybe there is a function that takes some predicate (isdigit, iseven, isdog) and splits given range to corresponding subrange?

2

There are 2 best solutions below

4
dfrib On BEST ANSWER

Using views::chunk_by to get a set of sub-views:

#include <cctype>
#include <fmt/ranges.h>
#include <ranges>
#include <string>

int main() {
  std::string_view plate_code{"kz083y68"};
  auto views = plate_code |
            std::views::chunk_by([](auto lhs, auto rhs) {
              // Thanks @Jarod42 for the simplified predicate!
              return std::isdigit(lhs) == std::isdigit(rhs);
            });
  for (auto const view : views) {
    fmt::print("{}", view);
  }  // ['k', 'z']['0', '8', '3']['y']['6', '8']
}

DEMO.

You can use views::join_with to get a single view with a selected delimiter:

int main() {
  std::string_view plate_code{"kz083y68"};
  auto view = plate_code | std::views::chunk_by([](auto lhs, auto rhs) {
                return std::isdigit(lhs) == std::isdigit(rhs);
              }) |
              std::views::join_with(' ');
  fmt::print("{}", view);
  // ['k', 'z', ' ', '0', '8', '3', ' ', 'y', ' ', '6', '8']
}

DEMO.

2
Enlico On

Just thought of sharing a way to make the already proposed solution a bit (imho) neater, closer to spoken English, only by using existing abstractions, specifically boost::hana::on from Boost.Hana and BOOST_HOF_LIFT from Boost.Hof, together with some using.

The buisness-logic line is just

auto views = plate_code | chunk_by(equal ^on^ is_digit);

It sounds a bit like: "chunk by equality applied on the whether each is_digit".

But equal is just a synonym for std::equal_to{},

constexpr auto equal = std::equal_to{};

and is_digit is an object wrapping std::isdigit by means of Boost.Hof,

constexpr auto is_digit = BOOST_HOF_LIFT(std::isdigit);

so no further logic was written by me, I've just reused existing building blocks.

Complete example here.