Как быстро заменить символы в массиве
Я использую XML Text reader в XML файле, который может содержать недопустимые для читателя символы. Моя первоначальная мысль заключалась в том, чтобы создать мою собственную версию считывателя потока и очистить плохие символы, но это сильно замедляет мою программу.
public class ClensingStream : StreamReader
{
private static char[] badChars = { '\x00', '\x09', '\x0A', '\x10' };
//snip
public override int Read(char[] buffer, int index, int count)
{
var tmp = base.Read(buffer, index, count);
for (int i = 0; i < buffer.Length; ++i)
{
//check the element in the buffer to see if it is one of the bad characters.
if(badChars.Contains(buffer[i]))
buffer[i] = ' ';
}
return tmp;
}
}
согласно моему профилировщику, код тратит 88% своего времени в if(badChars.Contains(buffer[i]))
, каков правильный способ сделать это, чтобы я не вызывал ужасную медлительность?
Ответы
Ответ 1
Причина, по которой он проводит столько времени в этой строке, состоит в том, что метод Contains
проходит через массив для поиска символа.
Поместите символы в HashSet<char>
вместо:
private static HashSet<char> badChars =
new HashSet<char>(new char[] { '\x00', '\x09', '\x0A', '\x10' });
Код для проверки, содержит ли набор символ, выглядит так же, как при поиске в массиве, но он использует хэш-код символа для поиска его вместо того, чтобы перебирать все элементы в массиве.
В качестве альтернативы вы можете поместить символы в переключатель, таким образом, компилятор создаст эффективное сравнение:
switch (buffer[i]]) {
case '\x00':
case '\x09':
case '\x0A':
case '\x10': buffer[i] = ' '; break;
}
Если у вас больше символов (пять или шесть IIRC), компилятор фактически создаст хеш-таблицу для поиска дел, так что это будет похоже на использование HashSet
.
Ответ 2
У вас могут быть лучшие результаты с помощью инструкции switch
:
switch (buffer[i])
{
case '\x00':
case '\x09':
case '\x0A':
case '\x10':
buffer[i] = ' ';
break;
}
Это должно быть скомпилировано до быстрого кода компилятором JIT во время выполнения. Черт, компилятор тоже может приблизиться. Вам также не нужен метод вызова.
Ответ 3
Вы можете использовать регулярные выражения для того, что должно быть оптимизировано. Прочитайте текст в строке и затем Replace
с вашими символами в регулярном выражении.
Однако ваш код также выглядит хорошо для меня, я думаю, регулярное выражение также не может сделать ничего, кроме поиска в вашем тексте... и вам нужно взять строку, в которой вам не нужно делать с другим варианты.
Ответ 4
вы можете проверить, насколько он оптимизирован, просто проверяя символы чтения, делая его
for (int i = index; i < index + count; i++){
//etc
}
Не знаю, насколько это вам поможет, вам нужно будет профилировать приложение реального мира, чтобы проверить
Ответ 5
Попробуйте преобразовать char[]
в строку, а затем с помощью IndexOfAny
.
Ответ 6
Вы можете использовать булевский массив
char[] badChars = { '\x00', '\x09', '\x0A', '\x10' };
char maxChar = badChars.Max();
Debug.Assert(maxChar < 256);
bool[] badCharsTable = new bool[maxChar + 1];
Array.ForEach(badChars, ch => badCharsTable[ch] = true);
и замените badChars.Contains(...)
на (ch < badCharsTable.Length && badCharsTable[ch])
.
Изменить: наконец, успел улучшить ответ.