Replace char with string with some string in place


I want to replace a character in a string with a string. can i do it locally? Because the newline is longer than the original line. Question: What can I do with an extra buffer? eg

void replaceChar(std::string &input, std::string replacementString, char charToReplace)
{
//some code here. No additional buffer
}

void main(){

  std::string input = "I am posting a comment on LinkedIn";
  std::string replacementString = "pppp";
  char charToReplace = 'o';
  replaceChar(input, replacementString, charToReplace);
}

      

I need a strategy (algorithm). it would be nice if the algorithm was designed with some language in mind that will not dynamically increase or decrease the length of the string once it is initialized, like C ++

+3


source to share


3 answers


std::string

has a member replace

, but it works in terms of numerical positions, not the previous content of the string. Thus, you usually have to combine it with a member find

in a loop, something like this:

std::string old("o");

int pos;

while ((pos = x.find(old)) != std::string::npos)
    x.replace(pos, old.length(), "pppp");

      

Personally, I rarely bothered about how often the line changes, but if that is a major concern, you can use std::count

to find the number of occurrences of the line old

, multiply by the size difference between the old and new lines, and use std::string::reserve()

enough space to reserve. Note, however, that it reserve

was added in C ++ 11 - older implementations will not have it.

Edit: While this does not apply to the strings you used as @ipc pointed out, it does not work correctly if the replacement string contains an instance of the value being replaced. If you need to deal with this, you need to specify the offset in the string where each search should start:



int pos = 0;

while ((pos = x.find(old, pos)) != std::string::npos) {
    x.replace(pos, old.length(), rep);
    pos += rep.length();
}

      

Or, you might prefer a loop for

in this case:

    std::string old("o");
    std::string rep("pop");

for (int pos=0; 
    (pos = x.find(old, pos)) != std::string::npos; 
    pos+=rep.length())
{
    x.replace(pos, old.length(), rep);
}

      

+4


source


I think you misunderstood the C ++ std :: string. This can actually change the length of the string dynamically. Allocates heaps internally and grows the buffer as needed.



+1


source


Here is some code that will minimize the number of assignments and allocations. It is based on the following answer to a similar question: fooobar.com/questions/1898689 / ...

Cases where the replacement string has length 0 or 1 are handled separately. Otherwise, the line should grow.

If the capacity is not enough, then anyway an external buffer is needed, so we just copy and replace.

An interesting case where the string already has sufficient capacity so we can actually do a non-trivial in-place replacement. We do this with reverse copy-replace, stopping when we don't need to replace anything.

This can be seen in the last line of the function.

void replaceChar(std::string& input, const std::string& replacementString, char charToReplace)
{
  if (replacementString.empty()) {
    input.erase(std::remove(input.begin(), input.end(), charToReplace), input.end());
    return;
  }
  if (replacementString.size() == 1) {
    std::replace(input.begin(), input.end(), charToReplace, replacementString.front());
    return;
  }

  const auto first_instance = std::find(input.begin(), input.end(), charToReplace);
  auto count = std::count(first_instance, input.end(), charToReplace);
  const auto extra_size = count * (replacementString.size() - 1);
  const auto new_size = input.size() + extra_size;

  if (input.capacity() < new_size) {
    std::string aux;
    aux.reserve(new_size);
    replace_with_range_copy(input.cbegin(), input.cend(), std::back_inserter(aux), charToReplace, replacementString.cbegin(), replacementString.cend());
    input.swap(aux);
    return;
  }

  input.resize(new_size);

  const auto rlast = std::make_reverse_iterator(first_instance);
  const auto rfirst = input.rbegin();
  const auto old_rfirst = rfirst + extra_size;

  replace_with_range_copy(old_rfirst, rlast, rfirst, charToReplace, replacementString.crbegin(), replacementString.crend());
}

      

Here is the implementation of the algorithm replace_with_range_copy

:

template <typename InputIt1, typename OutputIt, typename T, typename InputIt2>
OutputIt replace_with_range_copy(InputIt1 first, InputIt1 last, OutputIt d_first, const T& old_value, InputIt2 new_first, InputIt2 new_last)
{
  InputIt1 next;
  while (true) {
    if (first == last) return d_first;
    next = std::find(first, last, old_value);
    d_first = std::copy(first, next, d_first);
    if (next == last) return d_first;
    d_first = std::copy(new_first, new_last, d_first);
    first = std::next(next);
  }
}

      

0


source







All Articles