2

I know how to replace characters in vector using std::replace as in: ranges::replace

I would lie to do something similar with substrings in a string. E.g:

std::string str{"The quick brown fox jumped over the lazy dog.";
std::ranges::replace(str, "the", "a");

But I get an error:

error: no match for call to '(const std::ranges::__replace_fn) (std::string&, const char [4], const char [2])'

Or if I use strings

error: no match for call to '(const std::ranges::__replace_fn) (std::string&, std::string&, std::string&)'

It works for characters, but not for substrings.

Any ideas?

I have successfully used a loop and string.find and string.replace, but would like to use ranges.

Flip
  • 881
  • 7
  • 13
  • 1
    This contradicts the semantics of `std::ranges::replace`, as replace refers to *each element* in the given range. May be you want to split `str` by whitespace and then replace word by word using `std::ranges::replace`? – Patrick Roocks Mar 15 '23 at 10:20
  • Why not use string_view instead? – kiner_shah Mar 15 '23 at 10:41
  • 1
    Related https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string – 康桓瑋 Mar 15 '23 at 11:01
  • https://stackoverflow.com/questions/4643512/replace-substring-with-another-substring-c – Marek R Mar 15 '23 at 11:23
  • @Patrick Roocks Splitting the string would work if there is a useful delimiter, such as a space. Not always the case, but a useful suggestion to keep in mind. – Flip Mar 15 '23 at 15:14

1 Answers1

7

std::ranges::replace replaces elements in a range, you want to replace a subrange with another subrange. As far as I know, there is currently no algorithm for it in the standard library.

Using std::regex:

I would just use std::regex:

std::string str{"The quick brown fox jumped over the lazy dog."};
std::cout << std::regex_replace(str,std::regex("the"), "a") << std::endl;
The quick brown fox jumped over a lazy dog.

https://godbolt.org/z/q4c6dzzPP

Using std::ranges:

If you really really want to use std::ranges, you could split your range at the given pattern, insert the replacement at the splits and then join the range.

Using std::ranges in C++23:

C++23 conveniently provides a function std::views::join_with (that hasn't been widely adapted yet as of the time of writing this):

auto replace_view(std::string_view str, std::string_view pattern, std::string_view replacement)
{
    return str | std::views::split(pattern) | std::views::join_with(replacement);
}

https://godbolt.org/z/nd8zardE5

Using std::ranges in C++20:

You can get it to work in C++20 as well, you just need to put in a bit more work:

auto replace_view(std::string_view str, std::string_view pattern, std::string_view replacement)
{
    return str 
        // split at pattern
        | std::views::split(pattern) 
        // prefix each subrange with the replacement
        | std::views::transform(
            [replacement](auto const& substr){ 
                return std::string(replacement) + std::string(substr.begin(), substr.end()); 
            }
        )
        // join the subranges to a single range
        | std::views::join
        // drop the first occurance of the replacement
        | std::views::drop(replacement.size());
}

https://godbolt.org/z/j1WMWdW1h

joergbrech
  • 2,056
  • 1
  • 5
  • 17