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

Подстановочный знак регулярного выражения

У меня есть список около 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: Я совершенно не знаком с регулярными выражениями. Я знаю, насколько они могущественны, но я также знаю, что их очень сложно изучить. Так что я просто так и не успел сделать это...

4b9b3361

Ответ 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/....