Я хочу удалить все специальные символы из строки. Допустимые символы: AZ (верхний или нижний регистр), цифры (0-9), подчеркивание (_) или знак точки (.).
У меня есть следующее, это работает, но я подозреваю (я знаю!), Что это не очень эффективно:
public static string RemoveSpecialCharacters(string str)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
if ((str[i] >= '0' && str[i] <= '9')
|| (str[i] >= 'A' && str[i] <= 'z'
|| (str[i] == '.' || str[i] == '_')))
{
sb.Append(str[i]);
}
}
return sb.ToString();
}
Какой самый эффективный способ сделать это? Как будет выглядеть регулярное выражение, и как оно будет сравниваться с обычными манипуляциями со строками?
Строки, которые будут очищены, будут довольно короткими, обычно длиной от 10 до 30 символов.
Ответы:
Почему вы думаете, что ваш метод не эффективен? На самом деле это один из самых эффективных способов сделать это.
Конечно, вы должны прочитать символ в локальную переменную или использовать перечислитель, чтобы уменьшить количество обращений к массиву:
Одна вещь, которая делает такой метод эффективным, это то, что он хорошо масштабируется. Время выполнения будет зависеть от длины строки. Там нет неприятных сюрпризов, если вы будете использовать его на большой строке.
Редактировать:
я сделал быстрый тест производительности, выполняя каждую функцию по миллиону строк из 24 символов. Вот результаты:
Исходная функция: 54,5 мс.
Мое предлагаемое изменение: 47,1 мс.
Мой с настройкой StringBuilder емкость: 43,3 мс.
Регулярное выражение: 294,4 мс
Редактировать 2: я добавил различие между AZ и az в коде выше. (Я повторно тест производительности, и нет никакой заметной разницы.)
Редактировать 3:
я протестировал решение lookup + char [], и оно работает примерно за 13 мс.
Цена, которую нужно заплатить, - это, конечно, инициализация огромной таблицы поиска и ее сохранение в памяти. Ну, это не так много данных, но это много для такой тривиальной функции ...
источник
char[]
буфера, а неStringBuilder
, имеет небольшое преимущество по этому тестированию. (Мой менее читабельный, поэтому небольшое преимущество в производительности, вероятно, не стоит.)char[]
буфера работает (немного) лучше, чемStringBuilder
даже при масштабировании до строк длиной в десятки тысяч символов.Ну, если вам действительно не нужно выжимать производительность из вашей функции, просто делайте то, что проще всего поддерживать и понимать. Регулярное выражение будет выглядеть так:
Для дополнительной производительности вы можете либо предварительно скомпилировать его, либо просто указать, чтобы он компилировался при первом вызове (последующие вызовы будут быстрее).
источник
Я предлагаю создать простую таблицу поиска, которую вы можете инициализировать в статическом конструкторе, чтобы установить допустимую любую комбинацию символов. Это позволяет сделать быструю единую проверку.
редактировать
Также, для скорости, вы захотите инициализировать емкость вашего StringBuilder длиной вашей входной строки. Это позволит избежать перераспределения. Эти два метода вместе дадут вам скорость и гибкость.
другое редактирование
Я думаю, что компилятор мог бы оптимизировать его, но в качестве стиля и эффективности я рекомендую использовать foreach вместо for.
источник
for
иforeach
выдачи аналогичного кода. Я не знаю о строках, хотя. Я сомневаюсь, что JIT знает о массивоподобной природе String.источник
foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Регулярное выражение будет выглядеть так:
Но если производительность очень важна, я рекомендую вам сделать несколько тестов, прежде чем выбрать «путь регулярных выражений» ...
источник
Если вы используете динамический список символов, LINQ может предложить гораздо более быстрое и изящное решение:
Я сравнил этот подход с двумя из предыдущих «быстрых» подходов (компиляция релиза):
Обратите внимание, что алгоритм немного изменен - символы передаются в виде массива, а не жестко закодированы, что может немного повлиять на вещи (т. Е. / Другие решения будут иметь внутренний цикл foor для проверки массива символов).
Если я перейду к жестко запрограммированному решению, используя предложение LINQ where, то получится следующее:
Возможно, стоит взглянуть на LINQ или модифицированный подход, если вы планируете написать более общее решение, а не жестко программировать список символов. LINQ определенно дает вам краткий, хорошо читаемый код - даже больше, чем Regex.
источник
Я не уверен, что ваш алгоритм не эффективен. Это O (n) и смотрит на каждого персонажа только один раз. Вы не получите ничего лучше, если не будете волшебным образом знать значения перед их проверкой.
Однако я бы инициализировал емкость вашего
StringBuilder
к начальному размеру строки. Я предполагаю, что ваша проблема производительности связана с перераспределением памяти.Примечание: проверка
A
-z
не безопасно. Вы в том числе[
,\
,]
,^
,_
, и `...Примечание для стороны 2: Для этого дополнительного бита эффективности сравните порядок сравнений, чтобы минимизировать количество сравнений. (В худшем случае вы говорите о 8 сравнениях, поэтому не думайте слишком усердно.) Это меняется в зависимости от вашего ожидаемого вклада, но один пример может быть:
Примечание 3: Если по какой-либо причине вам ДЕЙСТВИТЕЛЬНО нужно, чтобы это было быстро, оператор switch может быть быстрее. Компилятор должен создать таблицу переходов для вас, что приведет только к одному сравнению:
источник
источник
Вы можете использовать регулярное выражение следующим образом:
источник
Мне кажется, это хорошо. Единственное улучшение, которое я хотел бы сделать, - это инициализация
StringBuilder
с длиной строки.источник
Я согласен с этим примером кода. Единственное другое это я превращаю в метод расширения строкового типа. Так что вы можете использовать его в очень простой строке или коде:
Спасибо Guffa за ваш эксперимент.
источник
Я бы использовал String Replace с Regular Expression для поиска «специальных символов», заменяя все найденные символы пустой строкой.
источник
Я должен был сделать что-то подобное для работы, но в моем случае мне пришлось отфильтровать все, что не является буквой, цифрой или пробелом (но вы могли легко изменить это в соответствии с вашими потребностями). Фильтрация выполняется на стороне клиента в JavaScript, но по соображениям безопасности я также выполняю фильтрацию на стороне сервера. Поскольку я могу ожидать, что большинство строк будут чистыми, я хотел бы избежать копирования строки, если в этом нет особой необходимости. Это позволит перейти к реализации ниже, которая должна работать лучше как для чистых, так и для грязных строк.
источник
Для S & G, Linq-ified способ:
Однако я не думаю, что это будет самый эффективный способ.
источник
источник
Использование:
И вы получите чистую строку
s
.erase()
удалит его из всех специальных символов и легко настраивается с помощьюmy_predicate()
функции.источник
HashSet is O (1)
Не уверен, что это быстрее, чем существующее сравнение
Я проверил, и это не быстрее, чем принятый ответ.
Я оставлю это так, как будто вам нужен настраиваемый набор символов, это было бы хорошим решением.
источник
Интересно, быстрее ли будет замена на основе регулярных выражений (возможно, скомпилированная).
Придется проверить, чтокто-то обнаружил, что это примерно в 5 раз медленнее.Кроме этого, вы должны инициализировать StringBuilder с ожидаемой длиной, чтобы промежуточную строку не нужно было копировать по мере роста.
Хорошее число - это длина исходной строки или немного меньше (в зависимости от характера входных данных функций).
Наконец, вы можете использовать справочную таблицу (в диапазоне 0..127), чтобы узнать, должен ли символ быть принят.
источник
Следующий код имеет следующий вывод (вывод заключается в том, что мы также можем сэкономить некоторые ресурсы памяти, выделяя массив меньшего размера):
Вы также можете добавить следующие строки кода для поддержки русской локали (размер массива будет 1104):
источник
Я не уверен, что это самый эффективный способ, но он работает для меня
источник
Здесь предлагается множество предлагаемых решений, некоторые из которых более эффективны, чем другие, но, возможно, не очень читабельны. Вот тот, который может быть не самым эффективным, но, безусловно, пригодным для использования в большинстве ситуаций, и он довольно лаконичен и удобен для чтения, используя Linq:
источник
источник
replaceAll
это не функция C # String, а либо Java, либо JavaScriptисточник
Если вы беспокоитесь о скорости, используйте указатели для редактирования существующей строки. Вы можете закрепить строку и получить указатель на нее, а затем запустить цикл for для каждого символа, заменив каждый недопустимый символ символом замены. Это было бы чрезвычайно эффективно и не потребовало бы выделения какой-либо новой строковой памяти. Вам также необходимо скомпилировать ваш модуль с опцией unsafe и добавить модификатор unsafe в заголовок вашего метода, чтобы использовать указатели.
источник