Как вы выполняете замену строки только на части строки? - PullRequest
1 голос
/ 18 января 2010

Мне нужен эффективный метод, который бы работал примерно так

РЕДАКТИРОВАТЬ: Извините, я не положил то, что я пытался раньше. Я обновил пример сейчас.

// Method signature, Only replaces first instance or how many are specified in max
public int MyReplace(ref string source,string org, string replace, int start, int max)
{
     int ret = 0;
     int len = replace.Length;
     int olen = org.Length;
     for(int i = 0; i < max; i++)
     {
          // Find the next instance of the search string
          int x = source.IndexOf(org, ret + olen);
          if(x > ret)
             ret = x;
          else
             break;

         // Insert the replacement
         source = source.Insert(x, replace);
         // And remove the original
         source = source.Remove(x + len, olen); // removes original string
     }
     return ret;
}

string source = "The cat can fly but only if he is the cat in the hat";
int i = MyReplace(ref source,"cat", "giraffe", 8, 1); 

// Results in the string "The cat can fly but only if he is the giraffe in the hat"
// i contains the index of the first letter of "giraffe" in the new string

Единственная причина, по которой я спрашиваю, заключается в том, что моя реализация будет медленной с 1000-ю заменами.

Ответы [ 5 ]

10 голосов
/ 18 января 2010

Как насчет:

public static int MyReplace(ref string source,
    string org, string replace, int start, int max)
{
    if (start < 0) throw new System.ArgumentOutOfRangeException("start");
    if (max <= 0) return 0;
    start = source.IndexOf(org, start);
    if (start < 0) return 0;
    StringBuilder sb = new StringBuilder(source, 0, start, source.Length);
    int found = 0;
    while (max-- > 0) {
        int index = source.IndexOf(org, start);
        if (index < 0) break;
        sb.Append(source, start, index - start).Append(replace);
        start = index + org.Length;
        found++;
    }
    sb.Append(source, start, source.Length - start);
    source = sb.ToString();
    return found;
}

он использует StringBuilder, чтобы избежать много промежуточных string с; Я не проверял это строго, но это похоже на работу. Он также пытается избежать лишних string, когда нет совпадений.

1 голос
/ 18 января 2010

Для начала попробуйте что-то вроде этого:

int count = 0;
Regex.Replace(source, Regex.Escape(literal), (match) =>
{
    return (count++ > something) ? "new value" : match.Value;
});
0 голосов
/ 29 июня 2012

Этот код работает на 100% быстрее, если у вас четыре замены, и примерно на 10% быстрее с одной заменой (быстрее по сравнению с опубликованным исходным кодом) Он использует указанный параметр запуска и работает при замене больших текстов на более мелкие.

Решение Mark Gravells (без обид ;-) на 60% медленнее исходного кода и также возвращает другое значение.

    // Method signature, Only replaces first instance or how many are specified in max
    public static int MyReplace(ref string source, string org, string replace, int    start, int max)
    {
        var ret = 0;
        int x = start;
        int reps = 0;
        int l = source.Length;
        int lastIdx = 0;
        string repstring = "";

        while (x < l)
        {
            if ((source[x] == org[0]) && (reps < max) && (x >= start))
            {
                bool match = true;
                for (int y = 1; y < org.Length; y++)
                {
                    if (source[x + y] != org[y])
                    {
                        match = false;
                        break;
                    }
                }
                if (match)
                {
                    repstring += source.Substring(lastIdx, x - lastIdx) + replace;
                    ret = x;
                    x += org.Length - 1;
                    reps++;
                    lastIdx = x + 1;
                    // Done?
                    if (reps == max)
                    {
                        source = repstring + source.Substring(lastIdx);
                        return ret;
                    }
                }
            }
            x++;
        }

        if (ret > 0)
        {
            source = repstring + source.Substring(lastIdx);
        }

        return ret;
    }
0 голосов
/ 29 июня 2012

У вас есть ошибка в том, что вы пропустите элемент для замены, если он в начале.

изменить эти строки;

  int ret = start;  // instead of zero, or you ignore the start parameter

  // Find the next instance of the search string
  // Do not skip olen for the first search!
  int x = i == 0 ? source.IndexOf(org, ret) : source.IndexOf(org, ret + olen);

Также ваша рутина выполняет 300 тысяч замен на секунду на моей машине. Вы уверены, что это будет узким местом?

И только что обнаружил, что у вашего кода также есть проблема, если вы заменяете большие тексты более мелкими.

0 голосов
/ 12 марта 2010

Для замены только первого совпадения:

    private string ReplaceFirst(string source, string oldString, string newString)
    {
        var index = source.IndexOf(oldString);
        var begin = source.Substring(0, index);
        var end = source.Substring(index + oldString.Length);
        return begin + newString + end;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...