Uso de Regex para generar cadenas en lugar de combinarlas

108

Estoy escribiendo una utilidad de Java que me ayuda a generar una gran cantidad de datos para las pruebas de rendimiento. Sería realmente genial poder especificar una expresión regular para Strings para que mi generador escupe cosas que coincidan con esto. ¿Hay algo ya horneado que pueda usar para hacer esto? ¿O hay una biblioteca que me lleva la mayor parte del camino?

Gracias

Andrew Harmel-Law
fuente
1
Aquí hay una biblioteca java útil que proporciona muchas características para usar expresiones regulares para generar cadenas (generación aleatoria, generar cadenas en función de su índice, generar todas las cadenas ...) compruébelo aquí
Mifmif
Otra alternativa podría ser esta
Vladislav Varslavans

Respuestas:

40

Editar: como se menciona en los comentarios, hay una biblioteca disponible en Google Code para lograr esto: https://code.google.com/archive/p/xeger/

Consulte también https://github.com/mifmif/Generex como lo sugiere Mifmif

Mensaje original:

En primer lugar, con una expresión regular lo suficientemente compleja, creo que esto puede ser imposible. Pero debería poder armar algo para expresiones regulares simples.

Si echas un vistazo al código fuente de la clase java.util.regex.Pattern, verás que usa una representación interna de instancias de Node. Cada uno de los diferentes componentes del patrón tiene su propia implementación de una subclase de Nodo. Estos nodos están organizados en un árbol.

Al producir un visitante que atraviese este árbol, debería poder llamar a un método generador sobrecargado o algún tipo de constructor que improvise algo.

Cheekysoft
fuente
2
No estoy seguro de que Xeger sea tan bueno. No puede manejar clases de personajes. No reconoce un simple [\w]. Una mirada a la última línea de su wiki nos dice eso.
John Red
2
Tenga en cuenta también que estos dependen, dk.brics.automatonasí que esté preparado para agregar dependencias pom de terceros. A la mayoría de la gente no le importa eso, pero me gustaría que hubiera algo un poco más compacto.
Sridhar Sarnobat
Existe una alternativa para xeger y generex. Carece de todos estos inconvenientes y no está obsoleto. Desplácese hacia abajo hasta mi respuesta.
Vladislav Varslavans
"En primer lugar, con una expresión regular lo suficientemente compleja, creo que esto puede ser imposible". - esto no es estrictamente cierto : cualquier expresión regular que pase contra algo también puede generar una entrada válida. Explicación: las expresiones regulares son de tipo 3 en la jerarquía de Chomsky, lo que significa que pueden expresarse como FSM. Al pasar por un FSM, cada borde se interpreta como una regla para el siguiente carácter, por lo que se puede utilizar un FSM para analizar o generar secuencias. Si un FSM tiene una ruta al terminal, se puede determinar una secuencia válida. Por lo tanto, solo es "imposible" si no hay una ruta a la terminal (que sería una expresión regular inútil).
Lawrence Wagerfield
22

Es demasiado tarde para ayudar al póster original, pero podría ayudar a un recién llegado. Generex es una biblioteca Java útil que proporciona muchas características para usar expresiones regulares para generar cadenas (generación aleatoria, generación de una cadena basada en su índice, generación de todas las cadenas ...).

Ejemplo:

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

Divulgar

El proyecto mencionado en esta publicación pertenece al usuario que responde (Mifmif) la pregunta. De acuerdo con las reglas , esto debe plantearse.

Mifmif
fuente
11
Parece que Generex es su propio proyecto. ¿Te importaría mencionar en tu publicación que este es tu propio proyecto, según las reglas aquí ?
Brian McCutchon
20

Xeger (Java) también es capaz de hacerlo:

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);
Wilfred Springer
fuente
1
Xeger funciona muy bien. PERO asegúrese de tener el tarro de autómata en la ruta de clases o en su pom / gradle
Delicia Brummitt
5

He ido a la raíz de rodar mi propia biblioteca para eso (en c # pero debería ser fácil de entender para un desarrollador de Java).

Rxrdg comenzó como una solución a un problema de creación de datos de prueba para un proyecto de la vida real. La idea básica es aprovechar los patrones de validación existentes (expresión regular) para crear datos aleatorios que se ajusten a dichos patrones. De esta forma se crean datos aleatorios válidos.

No es tan difícil escribir un analizador para patrones simples de expresiones regulares. Usar un árbol de sintaxis abstracto para generar cadenas debería ser aún más fácil.

Goran
fuente
el enlace ya no apunta al repositorio. Yo iría con openhub.net/p/rxrdg . Sin embargo, ¿la solución no se construye?
Veverke
4

En el podcast 11 de stackoverflow:

Spolsky: Sí. También hay un nuevo producto, si no desea utilizar el sistema de equipo, nuestros amigos de Redgate tienen un producto llamado SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . Cuesta $ 295 y solo genera algunos datos de prueba realistas. Y hace cosas como generar ciudades reales en la columna de la ciudad que realmente existen, y luego, cuando las genera, obtendrá el estado correcto, en lugar de equivocar el estado, o poner estados en ciudades alemanas y cosas como ... ya sabes, genera datos de aspecto bastante realista. No estoy seguro de cuáles son todas las funciones.

Probablemente esto no sea lo que está buscando, pero podría ser un buen punto de partida, en lugar de crear el suyo propio.

Parece que no puedo encontrar nada en Google, por lo que sugeriría abordar el problema analizando una expresión regular determinada en las unidades más pequeñas de trabajo (\ w, [xx], \ d, etc.) y escribiendo algunos métodos básicos para respaldar esas frases de expresión regular.

Entonces, para \ w tendría un método getRandomLetter () que devuelve cualquier letra aleatoria, y también tendría getRandomLetter (char startLetter, char endLetter) que le da una letra aleatoria entre los dos valores.

Craig
fuente
4

Esta pregunta es muy antigua, aunque el problema era real para mí. He intentado xeger y Generex y que no parece satisfacer mis reguirements. En realidad, fallan al procesar algunos de los patrones de expresiones regulares (como a{60000}) o para otros (por ejemplo (A|B|C|D|E|F)) simplemente no producen todos los valores posibles. Como no encontré ninguna otra solución adecuada, he creado mi propia biblioteca.

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

También hay artefacto en maven central disponible.

Ejemplo de uso:

RgxGen rgxGen = new RgxGen(aRegex);                     // Create generator
String s = rgxGen.generate();                           // Generate new random value
Vladislav Varslavans
fuente
3

Sé que ya hay una respuesta aceptada, pero he estado usando el generador de datos de RedGate (el que se menciona en la respuesta de Craig) y funciona MUY bien para todo lo que le he dicho. Es rápido y eso me deja con ganas de usar la misma expresión regular para generar los datos reales para cosas como códigos de registro que escupe esta cosa.

Se necesita una expresión regular como:

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

y genera toneladas de códigos únicos como:

LLK-32U

¿Es este un gran algoritmo secreto que RedGate descubrió y no tenemos suerte o es algo que nosotros, los simples mortales, realmente podríamos hacer?

J Wynia
fuente
3

Estoy en vuelo y acabo de ver la pregunta: he escrito la solución más fácil pero ineficiente e incompleta. Espero que pueda ayudarlo a comenzar a escribir su propio analizador:

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
fuente
Es posible que desee indicar qué tipo de cadenas se utilizan como entrada de patrón. En primer lugar, no es tan fácil determinar tales cosas a partir del código fuente. En segundo lugar, si hay errores o falta de claridad en el código fuente, no hay forma de saber si son intencionales o no.
Maarten Bodewes
StringTokenizer es una clase heredada que se conserva por razones de compatibilidad, aunque se desaconseja su uso en código nuevo. Se recomienda que cualquiera que busque esta funcionalidad use el método split de String o el paquete java.util.regex en su lugar.
Rohit
2

Tendrá que escribir su propio analizador, como lo hizo el autor de String :: Random (Perl). De hecho, no usa expresiones regulares en ningún lugar de ese módulo, es solo a lo que están acostumbrados los codificadores de perl.

Por otro lado, tal vez puedas echar un vistazo a la fuente para obtener algunas sugerencias.


EDITAR: Maldita sea, Blair me ganó en el golpe por 15 segundos.

Espo
fuente
1

Está lejos de admitir una expresión regular de PCRE completa, pero escribí el siguiente método de Ruby para tomar una cadena similar a una expresión regular y producir una variación de ella. (Para CAPTCHA basado en el idioma).

# 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

fuente
0

Si desea generar cadenas "críticas", puede considerar:

EGRET http://elarson.pythonanywhere.com/ que genera cadenas "malvadas" que cubren sus expresiones regulares

MUTREX http://cs.unibg.it/mutrex/ que genera cadenas de detección de fallas por mutación de expresiones regulares

Ambas son herramientas académicas (soy uno de los autores de la última) y funcionan razonablemente bien.

Angelo Gargantini
fuente