Генерация строк с критериями типа regex

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


Я не чувствую удобно с регулярным выражением: я не могу придумать начальный фрагмент кода, но я просто думаю о наивной реализации, использующей TList в качестве базового класса и использующей фильтр (регулярное выражение) против генерируемой строки "грубой силы".

какие еще оптимальные варианты ?


  • заказ: сначала по длине (сначала самый короткий), затем лексикографически.
  • спецификация ряда характеров, котор нужно использовать в поколении: все printable или любые возможная комбинация [A-Z], [a-z], чисел, специальных символов и, в конечном счете, пространства (регулярное выражение ?).
  • длина строки ограничена заданным Min / Max.
  • пространство поиска ограничено границами: начальная строка конечная строка с возможностью фильтрации (регулярное выражение ?)

Последние Изменения

для начала я перефразировал заголовок, используя regex как вместо регулярных выражений.

Я рассматриваю в пересмотреть первое требование как это открытая дверь, которая может привести к untractable вопрос.

Мне нужны предложения и помощь для правильной формулировки.

вторая мысль требования редактировать сделано. Все еще открыт для предложений для уточнения.

2 ответов


Я бы сделал это, построив минимум Детерминированный Конечный Автомат для языка. Если вы начинаете с регулярного выражения, это может быть сделано автоматически конструкцией Томпсона, а затем конструкцией подмножества и минимизацией. См.описание например.

С DFA в руке вы можете использовать что-то вроде этого алгоритма:

Let P = { < START, [""] > } be a set of pairs <State, list of strings>
for n = 0, 1, ... Max
  Let P' = {} be a new set 
  while P is not empty 
    Remove the pair <s, L> from P 
    For each transition s -- c --> t in alpha order of c
      if t is an accepting state,
          output l + c for each string l in L
      put <t, L + c> in P' (** i.e. append c to each string in L)
  end
  Set P = P'
end

обратите внимание, что этот шаг ознаменовал ** должен быть True set insertion, как дубликаты могут легко появиться.

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

редактировать

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

import java.util.Comparator;
import java.util.TreeSet;

public class Test{

    public static class DFA {

        public static class Transition  {

            final int to;
            final char lo, hi; // Character range.

            public Transition(int to, char lo, char hi) {
                this.to = to;
                this.lo = lo;
                this.hi = hi;
            }

            public Transition(int to, char ch) {
                this(to, ch, ch);
            }
        }

        // transitions[i] is a vector of transitions from state i.
        final Transition [] [] transitions;

        // accepting[i] is true iff state i is accepting
        final boolean [] accepting;

        // Make a fresh immutable DFA.
        public DFA(Transition [] [] transitions, boolean [] accepting) {
            this.transitions = transitions;
            this.accepting = accepting;
        }

        // A pair is a DFA state number and the input string read to get there.
        private static class Pair {
            final int at;
            final String s;

            Pair(int at, String s) {
                this.at = at;
                this.s = s;
            }
        }

        // Compare pairs ignoring `at` states, since 
        // they are equal iff the strings are equal.
        private Comparator<Pair> emitOrder = new Comparator<Pair>() {
            @Override
            public int compare(Pair a, Pair b) {
                return a.s.compareTo(b.s);
            }
        };

        // Emit all strings accepted by the DFA of given max length.
        // Output is in sorted order.
        void emit(int maxLength) {
            TreeSet<Pair> pairs = new TreeSet<Pair>(emitOrder);
            pairs.add(new Pair(0, ""));
            for (int len = 0; len <= maxLength; ++len) {
                TreeSet<Pair> newPairs = new TreeSet<Pair>(emitOrder);
                while (!pairs.isEmpty()) {
                    Pair pair = pairs.pollFirst();
                    for (Transition x : transitions[pair.at]) {
                        for (char ch = x.lo; ch <= x.hi; ch++) {
                            String s = pair.s + ch;
                            if (newPairs.add(new Pair(x.to, s)) && accepting[x.to]) {
                                System.out.println(s);
                            }
                        }
                    }
                }
                pairs = newPairs;
            }
        }
    }

    // Emit with a little DFA for floating point numbers.
    public void run() {
        DFA.Transition [] [] transitions = {
            {   // From 0
                new DFA.Transition(1, '-'),
                new DFA.Transition(2, '.'),
                new DFA.Transition(3, '0', '1'),
            },
            {   // From 1
                new DFA.Transition(2, '.'),
                new DFA.Transition(3, '0', '1'),
            },
            {   // From 2
                new DFA.Transition(4, '0', '1'),
            },
            {   // From 3
                new DFA.Transition(3, '0', '1'),
                new DFA.Transition(4, '.'),
            },
            {   // From 4
                new DFA.Transition(4, '0', '1'),
            }  
        };
        boolean [] accepting = { false, false, false, true, true };
        new DFA(transitions, accepting).emit(4);
    }

    public static void main (String [] args) {
        new Test().run();
    }
}

старый вопрос, но никто не ответил ему, Баунти и у меня уже есть готовое решение, вот возможный ответ:

Я однажды написал программы что делает это. Однако в C++ / Qt (хотя я пишу почти все свои программы на Delphi, это на C++), не поддерживает Unicode и не гарантирует порядок

это работает следующим образом:

  1. все ? {} + * | () операторы расширяются (до максимального предела), так что остаются только классы символов и обратные ссылки.

    например [a-c]+|t*|([x-z]){2}foo|(a|b)(t|u) становится [a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z][x-z])foo|at|au|bt|bu

    (последнее выражение | in - это просто обозначение, программа сохраняет каждый альтернативный subgregex в списке)

  2. обратные ссылки на несколько символов заменяются обратными ссылками на отдельные символы.

    например, выражение выше становится [a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z])([x-z])foo|at|au|bt|bu

    теперь каждый альтернативный subregex соответствует строке фиксированной длины.

  3. для каждого из вариантов, все комбинации выбора символов из классов печатаются:

    например, выражение выше становится a|b|c|aa|ba|..|cc|aaa|baa|...|ccc|aaaa|...|cccc||t|tt|tt|ttt|ttt|xxfooxx|yxfooyx|...|zzfoozz|at|au|bt|bu

вы вероятно можете добавить гарантии заказа шортлекс как следовать:

  1. сортировка символов в классах по алфавиту

  2. сортировка альтернатив, полученных на шаге 2. выше для длины

    (существует экспоненциально много альтернатив, но обычно их количество незначительно по сравнению с количеством результирующих строк)

  3. сортировка / обмен классами символов и обратными ссылками, так что каждая ссылка указывает назад

  4. перечислите возможные строки для одной альтернативы фиксированной длины, как и раньше, но начните с последнего класса символов, а не с первого, чтобы получить алфавитный заказ.

    (это не сработало бы, если бы были какие-либо обратные ссылки, указывающие вперед)

  5. если есть несколько альтернатив одинаковой длины, перечислите их в "параллели", сравните их текущие строки и выведите в алфавитном порядке самый низкий. (т. е. объединить уже отсортированные списки для каждой альтернативы.)

    это может быть оптимизировано, например, путем обнаружения различных префиксов и безопасных классов символов в суффиксе, которые могут быть перечислены без влияет на порядок. (например, a[a-z]|b[a-z] имеет различные префиксы, и [a-z] может быть перечислен без каких-либо сравнений)