Генерация строк с критериями типа regex
интересно, возможно ли реализовать оптимальный класс струнного генератора, отвечающий следующим требованиям второй мысли:
критерии генерации с использованием regexлексикографическом порядке перечисление.Граф propetryиндексированный доступ
Я не чувствую удобно с регулярным выражением: я не могу придумать начальный фрагмент кода, но я просто думаю о наивной реализации, использующей 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 и не гарантирует порядок
это работает следующим образом:
-
все ? {} + * | () операторы расширяются (до максимального предела), так что остаются только классы символов и обратные ссылки.
например
[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 в списке)
-
обратные ссылки на несколько символов заменяются обратными ссылками на отдельные символы.
например, выражение выше становится
[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 соответствует строке фиксированной длины.
-
для каждого из вариантов, все комбинации выбора символов из классов печатаются:
например, выражение выше становится
a|b|c|aa|ba|..|cc|aaa|baa|...|ccc|aaaa|...|cccc||t|tt|tt|ttt|ttt|xxfooxx|yxfooyx|...|zzfoozz|at|au|bt|bu
вы вероятно можете добавить гарантии заказа шортлекс как следовать:
сортировка символов в классах по алфавиту
-
сортировка альтернатив, полученных на шаге 2. выше для длины
(существует экспоненциально много альтернатив, но обычно их количество незначительно по сравнению с количеством результирующих строк)
сортировка / обмен классами символов и обратными ссылками, так что каждая ссылка указывает назад
-
перечислите возможные строки для одной альтернативы фиксированной длины, как и раньше, но начните с последнего класса символов, а не с первого, чтобы получить алфавитный заказ.
(это не сработало бы, если бы были какие-либо обратные ссылки, указывающие вперед)
-
если есть несколько альтернатив одинаковой длины, перечислите их в "параллели", сравните их текущие строки и выведите в алфавитном порядке самый низкий. (т. е. объединить уже отсортированные списки для каждой альтернативы.)
это может быть оптимизировано, например, путем обнаружения различных префиксов и безопасных классов символов в суффиксе, которые могут быть перечислены без влияет на порядок. (например, a[a-z]|b[a-z] имеет различные префиксы, и [a-z] может быть перечислен без каких-либо сравнений)