Точное количество совпадений нескольких букв - PullRequest
0 голосов
/ 12 февраля 2020

Для контекста я использую Mon goose и регулярное выражение для сопоставления строки в базе данных, используя find().

Учитывая пример строки {W}{W}{U}{U}{B}{B}{R}{R}{G}{G} Мне нужно сопоставить вхождения определенных букв. Я пытаюсь создать RegExp, который будет соответствовать только тогда, когда у меня есть необходимое количество букв.

{W}{W}{U}{U}{B}{B}{R}{R}{G}{G} => wwuubbrrgg, ggrrbbuuww, wuwubrbrgg и др. c

{W}{W}{U} => wwu, wuw, uww, et c

Решения, которые я обнаружил, не смогли объяснить порядок строки, являющейся несколько случайным, и несколько букв потенциально находились в одной скобке: {U/R}. Из-за этого я хочу принимать во внимание только реальные буквы и совпадать только тогда, когда найдено достаточное количество букв и не найдено ни одной буквы, которой нет.

1 Ответ

1 голос
/ 12 февраля 2020

Regex действительно очень плохо умеет считать. Требование определенного c номера указанного c символа без указания c порядка не является чем-то, в чем Regex очень хорош. Это может быть сделано, но не с какой-либо разумной мерой эффективности. В качестве примера, вот рабочее Regex для вашего сценария:

^(?=[^wW\n]*[wW][^wW\n]*[wW][^wW\n]*)(?=[^uU\n]*[uU][^uU\n]*[uU][^uU\n]*)(?=[^bB\n]*[bB][^bB\n]*[bB][^bB\n]*)(?=[^rR\n]*[rR][^rR\n]*[rR][^rR\n]*)(?=[^gG\n]*[gG][^gG\n]*[gG][^gG\n]*).{10}$

Как мы видим, это очень, очень долго для чего-то такого простого. Это потому, что это поведение не совсем то, для чего предназначен Regex, так как желаемая функциональность не является чем-то особенным. Я лично рекомендовал бы пройти и просто посчитать случаи каждого персонажа. Но, если вы не уверены в регулярных выражениях, вот разбивка:

^(?=[^wW\n]*[wW][^wW\n]*[wW][^wW\n]*)(?=[^uU\n]*[uU][^uU\n]*[uU][^uU\n]*)(?=[^bB\n]*[bB][^bB\n]*[bB][^bB\n]*)(?=[^rR\n]*[rR][^rR\n]*[rR][^rR\n]*)(?=[^gG\n]*[gG][^gG\n]*[gG][^gG\n]*).{10}$

^           //anchor to start of string
(?=         //start lookahead
   [^wW\n]* //any number of characters that aren't a 'w' or new line
   [wW]     //followed by the first instance of a character we're looking for
   [^wW\n]* //any number of characters that aren't a 'w' or new line
   [wW]     //followed by the second instance of a character we're looking for
   [^wW\n]* //any number of characters that aren't a 'w' or new line
)           //end lookahead
...         //repeat this for every character we want to be sure is in the string
.{10}       //now actually match the ten characters, now that we know the number of each is correct
$           //then validate that that takes us to the end of the string

EDIT : На самом деле, это регулярное выражение может быть немного уменьшено до:

^(?=[^wW\n]*[wW][^wW\n]*[wW])(?=[^uU\n]*[uU][^uU\n]*[uU])(?=[^bB\n]*[bB][^bB\n]*[bB])(?=[^rR\n]*[rR][^rR\n]*[rR])(?=[^gG\n]*[gG][^gG\n]*[gG]).{10}$

По сути, это просто избавляет от последней отрицательной группы захвата в каждом прогнозе. В этом нет необходимости, поскольку мы ограничиваем общую длину захвата до суммы, необходимой для каждого символа. Этого условия достаточно, чтобы знать, что мы удовлетворяем требованию не иметь более 2 любых символов. Тем не менее, я бы избегал решения этой проблемы с помощью регулярных выражений, поскольку за время, необходимое для генерации и запуска этого регулярного выражения для заданной комбинации символов, вы могли уже подсчитать количество экземпляров каждого символа и получить тот же результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...