Как превзойти эту regex замену по характеристикам?

После значительного измерения я определил горячую точку в одном из наших сервисов окон, которые я хотел бы оптимизировать. Мы обрабатываем строки, которые могут иметь несколько последовательных пробелов в нем, и мы хотели бы уменьшить только до одиночных пробелов. Мы используем скомпилированный regex помех для этой задачи:

private static readonly Regex 
    regex_select_all_multiple_whitespace_chars = 
        new Regex(@"\s+",RegexOptions.Compiled);

и затем используйте его следующим образом:

var cleanString=
    regex_select_all_multiple_whitespace_chars.Replace(dirtyString.Trim(), " ");

Эта строка вызывается несколько миллионов раз и оказывается довольно интенсивной. Я попытался записать что-то лучше, но я озадачен. Учитывая довольно скромные требования к обработке regex, конечно, быстрее существует что-то. Мог unsafe обработка с вещами скорости указателей далее?

Править:

Спасибо за удивительный набор ответов на этот вопрос... самый неожиданный!

7
задан spender 27 April 2010 в 11:18
поделиться

8 ответов

Это примерно в три раза быстрее:

private static string RemoveDuplicateSpaces(string text) {
  StringBuilder b = new StringBuilder(text.Length);
  bool space = false;
  foreach (char c in text) {
    if (c == ' ') {
      if (!space) b.Append(c);
      space = true;
    } else {
      b.Append(c);
      space = false;
    }
  }
  return b.ToString();
}
8
ответ дан 6 December 2019 в 06:02
поделиться

Массивы всегда будут быстрее

        public static string RemoveMultiSpace(string input)
    {
        var value = input;

        if (!string.IsNullOrEmpty(input))
        {
            var isSpace = false;
            var index = 0;
            var length = input.Length;
            var tempArray = new char[length];
            for (int i = 0; i < length; i++)
            {
                var symbol = input[i];
                if (symbol == ' ')
                {
                    if (!isSpace)
                    {
                        tempArray[index++] = symbol;
                    }
                    isSpace = true;
                }
                else
                {
                    tempArray[index++] = symbol;
                    isSpace = false;
                }
            }
            value = new string(tempArray, 0, index);
        }

        return value;
    }
0
ответ дан 6 December 2019 в 06:02
поделиться

В настоящее время вы заменяете одно пространство другим отдельным пространством. Попробуйте сопоставить \ s {2,} (или что-то подобное, если вы хотите заменить отдельные символы новой строки и другие символы).

6
ответ дан 6 December 2019 в 06:02
поделиться

Как это так простое выражение, заменяющее два или более пробелов одним пробелом, избавьтесь от объекта Regex и жестко запрограммируйте замену самостоятельно (в C ++ / CLI):

String ^text = "Some   text  to process";
bool spaces = false;
// make the following static and just clear it rather than reallocating it every time
System::Text::StringBuilder ^output = gcnew System::Text::StringBuilder;
for (int i = 0, l = text->Length ; i < l ; ++i)
{
  if (spaces)
  {
    if (text [i] != ' ')
    {
      output->Append (text [i]);
      spaces = false;
    }
  }
  else
  {
    output->Append (text [i]);
    if (text [i] == ' ')
    {
      spaces = true;
    }
  }
}
text = output->ToString ();
0
ответ дан 6 December 2019 в 06:02
поделиться

Как насчет этого ...

public string RemoveMultiSpace(string test)
{
var words = test.Split(new char[] { ' ' }, 
    StringSplitOptions.RemoveEmptyEntries);
return string.Join(" ", words);
}

Тестовый пример, запущенный с NUnit:
Время теста указано в миллисекундах.

Regex Test time: 338,8885
RemoveMultiSpace Test time: 78,9335
private static readonly Regex regex_select_all_multiple_whitespace_chars =
   new Regex(@"\s+", RegexOptions.Compiled);

[Test]
public void Test()
{
    string startString = "A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      ";
    string cleanString;
    Trace.WriteLine("Regex Test start");
    int count = 10000;
    Stopwatch timer = new Stopwatch();
    timer.Start();
    for (int i = 0; i < count; i++)
    {
        cleanString = regex_select_all_multiple_whitespace_chars.Replace(startString, " ");
    }
    var elapsed = timer.Elapsed;
    Trace.WriteLine("Regex Test end");
    Trace.WriteLine("Regex Test time: " + elapsed.TotalMilliseconds);

    Trace.WriteLine("RemoveMultiSpace Test start");
    timer = new Stopwatch();
    timer.Start();
    for (int i = 0; i < count; i++)
    {
        cleanString = RemoveMultiSpace(startString);
    }
    elapsed = timer.Elapsed;
    Trace.WriteLine("RemoveMultiSpace Test end");
    Trace.WriteLine("RemoveMultiSpace Test time: " + elapsed.TotalMilliseconds);
}

public string RemoveMultiSpace(string test)
{
    var words = test.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
    return string.Join(" ", words);
}

Редактировать:
Сделано еще несколько тестов и добавлен метод Гуффы "RemoveDuplicateSpaces", основанный на StringBuilder.
Итак, я пришел к выводу, что метод StringBuilder работает быстрее при большом количестве пробелов, но с меньшим количеством пробелов метод разделения строк работает немного быстрее.

Cleaning file with about 30000 lines, 10 iterations
RegEx time elapsed: 608,0623
RemoveMultiSpace time elapsed: 239,2049
RemoveDuplicateSpaces time elapsed: 307,2044

Cleaning string, 10000 iterations:
A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      A B  C   D    E     F      
RegEx time elapsed: 590,3626
RemoveMultiSpace time elapsed: 159,4547
RemoveDuplicateSpaces time elapsed: 137,6816

Cleaning string, 10000 iterations:
A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      A      B      C      D      E      F      
RegEx time elapsed: 290,5666
RemoveMultiSpace time elapsed: 64,6776
RemoveDuplicateSpaces time elapsed: 52,4732

7
ответ дан 6 December 2019 в 06:02
поделиться

Мне любопытно, как может работать прямая реализация:

    static string RemoveConsecutiveSpaces(string input)
    {
        bool whiteSpaceWritten = false;
        StringBuilder sbOutput = new StringBuilder(input.Length);

        foreach (Char c in input)
        {
            if (c == ' ')
            {
                if (!whiteSpaceWritten)
                {
                    whiteSpaceWritten = true;
                    sbOutput.Append(c);
                }
            }
            else
            {
                whiteSpaceWritten = false;
                sbOutput.Append(c);
            }
        }

        return sbOutput.ToString();
    }
3
ответ дан 6 December 2019 в 06:02
поделиться

Только одно предложение, если в ваших данных нет пробелов Unicode, вместо \ s + используйте [\ r \ n] + или [\ n] + или просто + (если есть только место), в основном ограничьте его минимальным набором символов.

3
ответ дан 6 December 2019 в 06:02
поделиться

Нельзя использовать регулярные выражения. Например:

private static string NormalizeWhitespace(string test)
{
    string trimmed = test.Trim();

    var sb = new StringBuilder(trimmed.Length);

    int i = 0;
    while (i < trimmed.Length)
    {
        if (trimmed[i] == ' ')
        {
            sb.Append(trimmed[i]);

            do { i++; } while (i < trimmed.Length && trimmed[i] == ' ');
        }

        sb.Append(trimmed[i]);

        i++;
    }

    return sb.ToString();
}

С помощью этого метода и следующего испытательного стенда:

private static readonly Regex MultipleWhitespaceRegex = new Regex(
    @"\s+", 
    RegexOptions.Compiled);

static void Main(string[] args)
{
    string test = "regex  select    all multiple     whitespace   chars";

    const int Iterations = 15000;

    var sw = new Stopwatch();

    sw.Start();
    for (int i = 0; i < Iterations; i++)
    {
        NormalizeWhitespace(test);
    }
    sw.Stop();
    Console.WriteLine("{0}ms", sw.ElapsedMilliseconds);

    sw.Reset();

    sw.Start();
    for (int i = 0; i < Iterations; i++)
    {
        MultipleWhitespaceRegex.Replace(test, " ");
    }
    sw.Stop();
    Console.WriteLine("{0}ms", sw.ElapsedMilliseconds);
}

Я получил следующие результаты:

// NormalizeWhitespace - 27ms
// Regex - 132ms

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

3
ответ дан 6 December 2019 в 06:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: