Подстановочный знак регулярного выражения
У меня есть список около 120 тысяч английских слов (в основном каждое слово на языке).
Мне нужно регулярное выражение, которое позволит искать эти слова с использованием символов подстановочных знаков, a.k.a. *
и ?
.
Несколько примеров:
- Если пользователь ищет
m?st*
, он будет соответствовать, например, master
или mister
или mistery
.
- если пользователь ищет
*ind
(любое слово, заканчивающееся на ind
), оно будет соответствовать wind
или bind
или blind
или grind
.
Теперь большинство пользователей (особенно те, кто не знаком с регулярными выражениями) знают, что ?
заменяет ровно 1 символ, а *
заменяет 0, 1 или более символов. Я абсолютно хочу создать свою функцию поиска на основе этого.
Мои вопросы: Как преобразовать то, что пользователь набирает (m?st*
например), в регулярное выражение?
Я искал в Интернете (очевидно, включая этот веб-сайт), и все, что я мог найти, было учебниками, которые пытались научить меня слишком много или вопросы, которые были несколько похожи, но недостаточно, чтобы дать ответ на мою собственную проблему.
Все, что я мог понять, это то, что мне нужно заменить ?
на .
. Итак, m?st*
становится m.st*
. Однако я понятия не имею, что заменить *
на.
Любая помощь будет принята с благодарностью. Спасибо.
PS: Я совершенно не знаком с регулярными выражениями. Я знаю, насколько они могущественны, но я также знаю, что их очень сложно изучить. Так что я просто так и не успел сделать это...
Ответы
Ответ 1
Если вы не хотите какого-нибудь смешного поведения, я бы рекомендовал использовать \w
вместо .
.
соответствует пробелам и другим символам, отличным от слов, которые вы, возможно, не захотите сделать.
Поэтому я заменил бы ?
на \w
и заменил *
на \w*
Также, если вы хотите, чтобы *
соответствовал хотя бы одному символу, вместо этого замените его на \w+
. Это означало бы, что ben*
будет соответствовать bend
и bending
, но не ben
- это зависит от вас, просто зависит от ваших требований.
Ответ 2
Замените ?
на .
и *
на .*
.
Ответ 3
- Заменить все '?' символов с '\ w'
- Заменить все символы '*' на '\ w *'
Оператор '*' повторяет предыдущий элемент '.' (любой символ) 0 или более раз.
Это предполагает, что ни одно из слов не содержит ".", "*" и "?".
Это хорошая рекомендация
http://www.regular-expressions.info/reference.html
Ответ 4
Вот способ преобразования шаблона в регулярное выражение:
- Подготовить все специальные символы ([{\ ^ - = $! |]}). + с \- поэтому они сопоставляются как символы и не делают неожиданный пользовательский опыт. Также вы можете заключить его в \Q (который запускает цитату) и \E (что его завершает). Также см. Параграф о безопасности.
- Заменить * подстановочный знак \S *
- Заменить? подстановочный знак с \S?
- Необязательно: шаблон префикса с ^ - это обеспечит точное совпадение с началом.
-
Необязательно: добавить $ к шаблону - это обеспечит точное совпадение с концом.
\S - обозначить непространственный символ, который имеет нулевой или более раз.
Рассмотрим используя неохотные (не жадные) кванторы, если у вас есть символы, которые будут совпадать после * или +. Это можно сделать, добавив ? после * или + следующим образом: \S *? и \S * +?
Рассмотрим безопасность: пользователь отправит вам код для запуска (поскольку регулярное выражение также является кодом, а пользовательская строка используется как регулярное выражение). Вы должны избегать передачи необработанного регулярного выражения в любые другие части приложения и использовать только для фильтрации данных, полученных другими средствами. Потому что, если вы делаете, то пользователь может повлиять на скорость вашего кода, предоставив различное регулярное выражение с подстановочной строкой - это может быть использовано в DoS-атаках.
Пример для показа скорости выполнения аналогичных шаблонов:
seq 1 50000000 > ~/1
du -sh ~/1
563M
time grep -P '.*' ~/1 &>/dev/null
6.65s
time grep -P '.*.*.*.*.*.*.*.*' ~/1 &>/dev/null
12.55s
time grep -P '.*..*..*..*..*.*' ~/1 &>/dev/null
31.14s
time grep -P '\S*.\S*.\S*.\S*.\S*\S*' ~/1 &>/dev/null
31.27s
Я бы предложил против использования. * просто потому, что он может соответствовать чему угодно, и обычно вещи разделяются пробелами.
Ответ 5
.
- это выражение, которое соответствует любому одному символу, как вы обнаружили. В ваши часы поиска вы, несомненно, также наткнулись на *
, который является оператором повторения, который при использовании после выражения соответствует предыдущему выражению ноль или более раз в строке.
Итак, эквивалент вашего значения *
заключается в том, чтобы свести эти два значения: .*
. Это означает "любой символ, равный нулю или более раз".
См. Учебное пособие по регулярным выражениям для операторов повторения.
Ответ 6
Замените *
на .*
(эквивалент регулярного выражения "0 или более любого символа" ).
Ответ 7
function matchWild(wild,name)
{
if (wild == '*') return true;
wild = wild.replace(/\./g,'\\.');
wild = wild.replace(/\?/g,'.');
wild = wild.replace(/\\/g,'\\\\');
wild = wild.replace(/\//g,'\\/');
wild = wild.replace(/\*/g,'(.+?)');
var re = new RegExp(wild,'i');
return re.test(name);
}
Ответ 8
Это то, что я использую:
String wildcardToRegex(String wildcardString) {
// The 12 is arbitrary, you may adjust it to fit your needs depending
// on how many special characters you expect in a single pattern.
StringBuilder sb = new StringBuilder(wildcardString.length() + 12);
sb.append('^');
for (int i = 0; i < wildcardString.length(); ++i) {
char c = wildcardString.charAt(i);
if (c == '*') {
sb.append(".*");
} else if (c == '?') {
sb.append('.');
} else if ("\\.[]{}()+-^$|".indexOf(c) >= 0) {
sb.append('\\');
sb.append(c);
} else {
sb.append(c);
}
}
sb.append('$');
return sb.toString();
}
Список специальных символов из fooobar.com/info/82881/....