3


我想用字符串替换字符串中的字符。我可以就地做吗?由于新字符串的长度大于原始字符串。问题是我可以使用额外的缓冲区吗?例如

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

我只想要策略(算法)。如果算法的设计能够牢记某种语言,一旦它像 c++ 一样被初始化就不会动态增加或减少字符串长度,那将是很好的

4

4 回答 4

6

std::string有一个replace成员,但它根据数字位置工作,而不是字符串的先前内容。因此,您通常必须将它与find循环中的成员结合起来,如下所示:

std::string old("o");

int pos;

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

就个人而言,我很少关心字符串调整大小的频率,但如果这是一个主要问题,您可以使用std::count查找old字符串的出现次数,乘以新旧字符串之间的大小差异,然后用来std::string::reserve()预留足够的空间。但是请注意,这reserve是在 C++11 中添加的——旧的实现不会有它。

编辑:虽然这与您使用的字符串无关,正如@ipc 指出的那样,但如果替换字符串包含被替换值的实例,这将无法正常工作。如果您需要处理这个问题,您需要在开始每次搜索的字符串中提供偏移量:

int pos = 0;

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

或者,for在这种情况下,您可能更喜欢循环:

    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);
}
于 2012-04-05T15:05:50.547 回答
1

我认为您误解了 C++ std::string。它实际上可以动态更改字符串长度。在内部进行堆分配,并在必要时增加缓冲区。

于 2012-04-05T14:47:32.357 回答
1

这是一个最小化分配和分配数量的代码。它基于以下对类似问题的回答: https ://stackoverflow.com/a/32322122/3903076

替换字符串长度为 0 或 1 的情况分别处理。否则,字符串必须增长。

如果没有足够的容量,那么无论如何都需要一个外部缓冲区,所以我们只需进行复制替换和交换。

有趣的情况是当字符串已经有足够的容量时,我们实际上可以进行非平凡的就地替换。我们通过反向复制替换来做到这一点,当我们不需要替换任何其他内容时停止。

这可以在函数的最后一行看到。

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

下面是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);
  }
}
于 2016-02-12T19:43:17.470 回答
0

我尝试了这种老式的东西,我认为它有效。这里是。我不确定这是否适用于 ascii 以外的编码。

#include <string>
#include <cstring>

std::string
replace_char_with_string
    (const char *in_p,
     char  from_ch,
     const char *to_p)
{
    char output_c_str[strlen(in_p)*2+1], *out_p = output_c_str;
    int  to_len = strlen(to_p);

    while (*in_p)
    {
        if (*in_p == from_ch)
        {
            strcpy(out_p, to_p);
            out_p += to_len;
        }
        else
        {
            *out_p++ = *in_p;
        }

        ++in_p;
    }
    *out_p = '\0';

    std::string output(output_c_str);
    return output;
}

// example usage
std::string java_namespace_name = "com.foo.boo";
std::string cpp_namespace_name = replace_char_with_string(java_namespace_name.c_str()
于 2018-09-17T01:52:48.900 回答