Использование Regex для генерации строк вместо сопоставления с ними

111

Я пишу утилиту Java, которая помогает мне генерировать множество данных для тестирования производительности. Было бы действительно здорово иметь возможность указать регулярное выражение для строк, чтобы мой генератор выдавал то, что соответствует этому. Есть ли что-то уже запеченное, что я могу использовать для этого? Или есть библиотека, которая помогает мне больше всего?

Спасибо

Эндрю Хармель-Лоу
источник
1
Вот полезная java-библиотека, которая предоставляет множество функций для использования регулярных выражений для генерации String (случайная генерация, генерация String на основе ее индекса, генерация всей String ..). Проверьте это здесь
Mifmif
Другой альтернативой может быть это
Владислав Варславанс

Ответы:

43

Изменить: как упоминалось в комментариях, для этого в Google Code доступна библиотека: https://code.google.com/archive/p/xeger/

См. Также https://github.com/mifmif/Generex, предложенный Mifmif

Оригинал сообщения:

Во-первых, я считаю, что с достаточно сложным регулярным выражением это может быть невозможно. Но вы должны уметь собрать что-нибудь для простых регулярных выражений.

Если вы посмотрите на исходный код класса java.util.regex.Pattern, вы увидите, что он использует внутреннее представление экземпляров Node. Каждый из различных компонентов шаблона имеет собственную реализацию подкласса Node. Эти узлы организованы в виде дерева.

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

Cheekysoft
источник
2
Я не уверен, что Xeger настолько хорош. Он не может обрабатывать классы символов. Он не может распознать простой [\w]. Об этом нам говорит последняя строка их вики .
Джон Ред
2
Также обратите внимание, что они зависят от этого, dk.brics.automatonпоэтому будьте готовы добавить сторонние зависимости pom. Большинство людей не возражают против этого, но я бы хотел, чтобы было что-то более компактное.
Шридхар Сарнобат
Есть альтернатива xeger и generex. Он лишен всех этих недостатков и не устарел. Прокрутите вниз до моего ответа.
Владислав Варславанс
«Во-первых, я считаю, что с достаточно сложным регулярным выражением это может быть невозможно». - это не совсем так : любое регулярное выражение, которое передает что-то, также может генерировать допустимый ввод. Объяснение: регулярные выражения относятся к типу 3 в иерархии Хомского, что означает, что они могут быть выражены как конечные автоматы. При пошаговом прохождении конечного автомата каждое ребро интерпретируется как правило для следующего символа, поэтому конечный автомат можно использовать для синтаксического анализа или генерации последовательностей. Если у конечного автомата есть путь к терминалу, можно определить допустимую последовательность. Таким образом, это «невозможно», только если нет пути к терминалу (что было бы бесполезным регулярным выражением).
Лоуренс Вагерфилд,
22

Слишком поздно помогать оригинальному плакату, но он может помочь новичку. Generex - полезная библиотека Java, которая предоставляет множество функций для использования регулярных выражений для генерации строк (случайное создание, генерация строки на основе ее индекса, генерация всех строк ...).

Пример :

Generex generex = new Generex("[0-3]([a-c]|[e-g]{1,2})");

// generate the second String in lexicographical order that matches the given Regex.
String secondString = generex.getMatchedString(2);
System.out.println(secondString);// it print '0b'

// Generate all String that matches the given Regex.
List<String> matchedStrs = generex.getAllMatchedStrings();

// Using Generex iterator
Iterator iterator = generex.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
}
// it prints 0a 0b 0c 0e 0ee 0e 0e 0f 0fe 0f 0f 0g 0ge 0g 0g 1a 1b 1c 1e
// 1ee 1e 1e 1f 1fe 1f 1f 1g 1ge 1g 1g 2a 2b 2c 2e 2ee 2e 2e 2f 2fe 2f 2f 2g
// 2ge 2g 2g 3a 3b 3c 3e 3ee 3e 3e 3f 3fe 3f 3f 3g 3ge 3g 3g 1ee

// Generate random String
String randomStr = generex.random();
System.out.println(randomStr);// a random value from the previous String list

Раскрытие

Проект, упомянутый в этом посте, принадлежит пользователю, отвечающему (Mifmif) на вопрос. По правилам , это нужно поднять.

Мифмиф
источник
11
Похоже, Generex - ваш собственный проект. Не могли бы вы упомянуть в своем посте, что это ваш собственный проект в соответствии с правилами здесь ?
Брайан МакКатчон 02
20

Xeger (Java) тоже может это делать:

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);
Уилфред Спрингер
источник
1
Ксегер прекрасно работает. НО убедитесь, что у вас есть автомат jar на пути к классу или в вашем pom / gradle
Delicia Brummitt
5

Я пошел в корень, развернув для этого свою собственную библиотеку (на C #, но это должно быть легко понять для Java-разработчика).

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

Написать парсер для простых шаблонов регулярных выражений не так уж и сложно. Использование абстрактного синтаксического дерева для создания строк должно быть еще проще.

Горан
источник
ссылка больше не указывает на репозиторий. Я бы пошел с openhub.net/p/rxrdg . Однако решение не построить?
Veverke
4

В подкасте stackoverflow 11:

Спольски: Ага. Также есть новый продукт, если вы не хотите использовать Team System, у наших друзей в Redgate есть продукт под названием SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . Это 295 долларов, и он просто генерирует некоторые реалистичные тестовые данные. И он делает такие вещи, как фактическое создание реальных городов в столбце городов, которые действительно существуют, а затем, когда он генерирует их, он получает правильное состояние, вместо того, чтобы ошибаться в состоянии или помещать штаты в немецкие города и тому подобное ... вы знаете, он генерирует довольно реалистичные данные. Я не совсем уверен, каковы все функции.

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

Мне кажется, что я ничего не могу найти в Google, поэтому я предлагаю решить эту проблему путем разбора данного регулярного выражения на мельчайшие единицы работы (\ w, [xx], \ d и т. Д.) И написания некоторых основных методов для поддержки эти фразы регулярного выражения.

Итак, для \ w у вас будет метод getRandomLetter (), который возвращает любую случайную букву, и у вас также будет getRandomLetter (char startLetter, char endLetter), который дает вам случайную букву между двумя значениями.

Крейг
источник
4

Вопрос действительно старый, хотя для меня проблема была актуальна. Я пробовал xeger и Generex, и, похоже, они не соответствуют моим требованиям. На самом деле они не могут обрабатывать некоторые шаблоны регулярных выражений (например a{60000}), а для других (например (A|B|C|D|E|F)) они просто не создают всех возможных значений. Поскольку другого подходящего решения я не нашел - создал свою библиотеку.

https://github.com/curious-odd-man/RgxGen

Также есть артефакт на центральном сервере maven.

Пример использования:

RgxGen rgxGen = new RgxGen(aRegex);                     // Create generator
String s = rgxGen.generate();                           // Generate new random value
Владислав Варславанс
источник
3

Я знаю, что есть уже принятый ответ, но я использую генератор данных RedGate (тот, который упоминается в ответе Крейга), и он ДЕЙСТВИТЕЛЬНО работает для всего, что я ему бросил. Это быстро, и поэтому мне хочется использовать одно и то же регулярное выражение для генерации реальных данных для таких вещей, как коды регистрации, которые эта штука выплевывает.

Требуется регулярное выражение вроде:

[A-Z0-9]{3,3}-[A-Z0-9]{3,3}

и генерирует множество уникальных кодов, например:

LLK-32U

Это какой-то большой секретный алгоритм, который разработал RedGate, и нам всем не повезло, или это то, что мы, простые смертные, действительно могли сделать?

J Wynia
источник
3

Я в полете и только что увидел вопрос: я написал самое простое, но неэффективное и неполное решение. Надеюсь, это поможет вам начать писать собственный парсер:

public static void main(String[] args) {

    String line = "[A-Z0-9]{16}";
    String[] tokens = line.split(line);
    char[] pattern = new char[100];
    int i = 0;
    int len = tokens.length;
    String sep1 = "[{";
    StringTokenizer st = new StringTokenizer(line, sep1);

    while (st.hasMoreTokens()) {
        String token = st.nextToken();
        System.out.println(token);

        if (token.contains("]")) {
            char[] endStr = null;

            if (!token.endsWith("]")) {
                String[] subTokens = token.split("]");
                token = subTokens[0];

                if (!subTokens[1].equalsIgnoreCase("*")) {
                    endStr = subTokens[1].toCharArray();
                }
            }

            if (token.startsWith("^")) {
                String subStr = token.substring(1, token.length() - 1);
                char[] subChar = subStr.toCharArray();
                Set set = new HashSet<Character>();

                for (int p = 0; p < subChar.length; p++) {
                    set.add(subChar[p]);
                }

                int asci = 1;

                while (true) {
                    char newChar = (char) (subChar[0] + (asci++));

                    if (!set.contains(newChar)) {
                        pattern[i++] = newChar;
                        break;
                    }
                }
                if (endStr != null) {
                    for (int r = 0; r < endStr.length; r++) {
                        pattern[i++] = endStr[r];
                    }
                }

            } else {
                pattern[i++] = token.charAt(0);
            }
        } else if (token.contains("}")) {
            char[] endStr = null;

            if (!token.endsWith("}")) {
                String[] subTokens = token.split("}");
                token = subTokens[0];

                if (!subTokens[1].equalsIgnoreCase("*")) {
                    endStr = subTokens[1].toCharArray();
                }
            }

            int length = Integer.parseInt((new StringTokenizer(token, (",}"))).nextToken());
            char element = pattern[i - 1];

            for (int j = 0; j < length - 1; j++) {
                pattern[i++] = element;
            }

            if (endStr != null) {
                for (int r = 0; r < endStr.length; r++) {
                    pattern[i++] = endStr[r];
                }
            }
        } else {
            char[] temp = token.toCharArray();

            for (int q = 0; q < temp.length; q++) {
                pattern[i++] = temp[q];
            }
        }
    }

    String result = "";

    for (int j = 0; j < i; j++) {
        result += pattern[j];
    }

    System.out.print(result);
}
R dhabalia
источник
Вы можете указать, какие строки используются в качестве входных данных шаблона. Во-первых, не так-то просто определить такие вещи из исходного кода. Во-вторых, если в исходном коде есть какие-либо ошибки или неточности, невозможно узнать, преднамеренные они или нет.
Maarten Bodewes
StringTokenizer - это устаревший класс, который сохранен по соображениям совместимости, хотя его использование не рекомендуется в новом коде. Рекомендуется, чтобы любой, кто ищет эту функциональность, использовал вместо этого метод разделения String или пакет java.util.regex.
Rohit
2

Вам придется написать свой собственный парсер, как это сделал автор String :: Random (Perl). Фактически, он нигде в этом модуле не использует регулярные выражения, это просто то, к чему привыкли Perl-кодеры.

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


РЕДАКТИРОВАТЬ: Черт, Блэр опередила меня на 15 секунд.

Espo
источник
1

Он далек от поддержки полного регулярного выражения PCRE, но я написал следующий метод Ruby, чтобы взять строку, похожую на регулярное выражение, и создать ее вариант. (Для CAPTCHA на основе языка.)

# q = "(How (much|many)|What) is (the (value|result) of)? :num1 :op :num2?"
# values = { :num1=>42, :op=>"plus", :num2=>17 }
# 4.times{ puts q.variation( values ) }
# => What is 42 plus 17?
# => How many is the result of 42 plus 17?
# => What is the result of 42 plus 17?
# => How much is the value of 42 plus 17?
class String
  def variation( values={} )
    out = self.dup
    while out.gsub!( /\(([^())?]+)\)(\?)?/ ){
      ( $2 && ( rand > 0.5 ) ) ? '' : $1.split( '|' ).random
    }; end
    out.gsub!( /:(#{values.keys.join('|')})\b/ ){ values[$1.intern] }
    out.gsub!( /\s{2,}/, ' ' )
    out
  end
end

class Array
  def random
    self[ rand( self.length ) ]
  end
end

источник
1

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

Эверетт
источник
0

Если вы хотите сгенерировать «критические» строки, вы можете рассмотреть:

EGRET http://elarson.pythonanywhere.com/, который генерирует «злые» строки, покрывающие ваши регулярные выражения.

MUTREX http://cs.unibg.it/mutrex/, который генерирует строки обнаружения ошибок путем мутации регулярного выражения

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

Анджело Гаргантини
источник