Тестирование для "EndsWith" эффективно с помощью Regex

Мне нужно создать синтаксис Regex (.NET), чтобы определить, заканчивается ли строка с определенным значением. В частности, мне нужно проверить, имеет ли файл определенное расширение (или набор расширений).

Код, который я пытаюсь исправить, использовал:

.*\.(png|jpg|gif)$

который ужасно медленный для неудачных совпадений в моем сценарии (предположительно из-за обратного отслеживания.

Простое удаление .* (это хорошо, поскольку API проверяет только совпадения и ничего не извлекает) в начале делает регулярное выражение намного более эффективным.

По-прежнему кажется, что он довольно неэффективен. Я пропустил что-то очевидное здесь?

К сожалению, я не управляю рассматриваемым API, поэтому мне нужно регулярное выражение, чтобы сделать это, хотя я обычно не рассматривал регулярное выражение как правильный инструмент для задания.

Я также провел несколько тестов с помощью RegexOptions.RightToLeft и обнаружил, что я могу выжать немного больше производительности из своего тестового примера с помощью ^.*\.(png|jpg|gif)$, но я не могу найти способ указать параметр RightToLeft в пределах строка самого регулярного выражения, поэтому я не думаю, что могу ее использовать.

Ответ 1

У меня нет доступа к С#, поэтому я не могу попробовать это... но вы должны избегать слишком большого возврата, заставляя движок сначала находить конец строки, а затем сопоставляя расширения:

$(?<=\.(gif|png|jpg))

Я не уверен в эффекте, который имеет внешний вид при работе.

Ответ 2

Действительно, вы можете просто полностью удалить Regex и использовать String.EndsWidth со следующим:

var extensions = new String[] { ".png", ".jpg", ".gif" };
extensions.Any(ext => "something".EndsWith(ext));

У меня обычно возникает ощущение, что быстрее использовать простые строковые функции для таких случаев, а не пытаться найти эффективный способ использования эффективного регулярного выражения с точки зрения времени выполнения и/или времени разработки, если только вы не используете удобно и хорошо знать, что эффективно с точки зрения Regex.

Ответ 3

Заставить его смотреть конкретно на период, а не на любой символ, предшествующий расширению:

\.(png|jpg|gif)$

Это сделает его более безопасным (не будет соответствовать x.xgif), и ему не придется вообще отказываться, пока не найдет период (в отличие от обратного отслеживания для каждого символа).

Ответ 4

Если вы можете изменить код, почему вы не можете использовать что-то еще? Вы не контролируете API, правильно, но вы все равно меняете его. Это я действительно не понимаю.

В любом случае, почему бы не просто:

var AcceptedExtensions = new List<string>() { "txt", "html", "htm" };
var extension = filename.Substring(filename.LastIndexOf(".") + 1).ToLower();
return AcceptedExtensions.Contains(extension);

IEnumerable AcceptedExtensions будет загружен из некоторой конфигурации, так же, как вы загрузите jpg|gif|.... Или это будет постоянным, что угодно. Вам просто не нужно воссоздавать его каждый раз, когда вы собираетесь его использовать (я сомневаюсь, что это будет узким местом, хотя).

Ответ 5

Вам, вероятно, не нужно регулярное выражение для этого... но с оригинальным вопросом:

Убедитесь, что вы используете RegexOptions.Compiled, чтобы предварительно скомпилировать регулярное выражение, а затем повторно использовать объект RegEx. Это позволяет избежать установки RegEx каждый раз, когда вы его используете, это значительно ускорит процесс.