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

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

Спасибо

11 ответов

Решение

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

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

Смотрите также https://github.com/mifmif/Generex в соответствии с предложением Mifmif

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

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

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

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

Уже слишком поздно, чтобы помочь оригинальному постеру, но это может помочь новичку. 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

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

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);

Вопрос действительно старый, хотя для меня проблема была актуальна. Я пробовал 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

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

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

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

Visual Studio Team System включает в себя что-то вроде этого. Проверьте это

Не так много помощи для Java, так что извините.

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

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

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

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

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

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

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);
}

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

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

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

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

LLK-32U

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

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

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


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

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

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

# 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

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

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

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

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

Другие вопросы по тегам