После значительного измерения я определил горячую точку в одном из наших сервисов окон, которые я хотел бы оптимизировать. Мы обрабатываем строки, которые могут иметь несколько последовательных пробелов в нем, и мы хотели бы уменьшить только до одиночных пробелов. Мы используем скомпилированный 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
обработка с вещами скорости указателей далее?
Править:
Спасибо за удивительный набор ответов на этот вопрос... самый неожиданный!
Это примерно в три раза быстрее:
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();
}
Массивы всегда будут быстрее
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;
}
В настоящее время вы заменяете одно пространство другим отдельным пространством. Попробуйте сопоставить \ s {2,}
(или что-то подобное, если вы хотите заменить отдельные символы новой строки и другие символы).
Как это так простое выражение, заменяющее два или более пробелов одним пробелом, избавьтесь от объекта 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 ();
Как насчет этого ...
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
Мне любопытно, как может работать прямая реализация:
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();
}
Только одно предложение, если в ваших данных нет пробелов Unicode, вместо \ s +
используйте [\ r \ n] +
или [\ n] +
или просто +
(если есть только место), в основном ограничьте его минимальным набором символов.
Нельзя использовать регулярные выражения. Например:
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
и предоставляется только для того, чтобы сделать так, чтобы регулярные выражения иногда не были лучшим ответом.