Совпадение регулярных выражений с набором символов без предыдущего дублирования

#javascript #regex #text #dynamic #match

#javascript #регулярное выражение #текст #динамический #совпадение

Вопрос:

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

 character set: abcd
character format: ??j? (question mark represents a a character from character set)
  

Пример

 abjd = match
bdja = match
dbja = match
ab = no match
aajd = no match
abjdd = no match
abj = no match
  

Я создал конструктор регулярных выражений (на js) следующим образом:

 // characters are the character set
// wordFormat is the character format
// replace(str, search, replacement) replaces search in str with replacement
var chars = "["   characters   "]{1}";
var afterSpecialConversion = replace(wordFormat, "?", chars);

var myRegex = new RegExp("^"   afterSpecialConversion   "$", "gi");
  

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

Также данный набор символов aabcd now a может существовать дважды. Есть какие-нибудь предложения?

Ответ №1:

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

Я привел пример регулярного выражения в Regex101 для демонстрации в вашем вопросе.

Более общий принцип заключается в замене каждого набора n вопросительных знаков шаблоном, соответствующим этому:

 (?:([<chars>])(?!.*<m>)){<n>}
  

Где <chars> — набор символов, который вы хотите использовать, m — индекс набора вопросительных знаков (начиная с 1 — подробнее об этом чуть позже), а <n> — количество вопросительных знаков в группе. Это приводит к коду regex-builder, который выглядит следующим образом:

 function getRe(pattern, chars) {
    var re = "^";
    var qMarkGroup = 1;
    var qMarkCount = 0;

    for (var index in pattern) {
        var char = pattern[index];
        if (char === "?") {
            qMarkCount  = 1;
        } else {
            if (qMarkCount > 0) {
                re  = "(?:(["   chars   "])(?!.*\"   qMarkGroup   ")){"   qMarkCount   "}"   char;
                qMarkCount = 0;
                qMarkGroup  = 1;
            }
        }
    }

    // Need to do this again in case we have a group of question marks at the end of the pattern
    if (qMarkCount > 0) {
        re  = "(?:(["   chars   "])(?!.*\"   qMarkGroup   ")){"   qMarkCount   "}";
    }
    re  = "$";
    return new Regexp(re, "gi");
}
  

Демонстрация кода на Repl.it

Очевидно, что это определение функции очень подробное, чтобы продемонстрировать задействованные принципы. Не стесняйтесь использовать его в гольф (но не забывайте следить за проблемами с оградой, которые я описал в комментариях).

Кроме того, обязательно очистите входные данные. Это пример, который сломается, если кто-то, например, вставит ] в chars .