Utilizzo di Regex per generare stringhe anziché abbinarle


108

Sto scrivendo un'utilità Java che mi aiuta a generare carichi di dati per il test delle prestazioni. Sarebbe davvero bello poter specificare una regex per Strings in modo che il mio generatore sputi cose che corrispondono a questo. C'è qualcosa là fuori già cotto che posso usare per farlo? O c'è una biblioteca che mi porta quasi sempre lì?

Grazie


1
Ecco un'utile libreria java che fornisce molte funzionalità per l'utilizzo di regex per generare String (generazione casuale, generare String in base al suo indice, generare tutte le String ..) dai un'occhiata qui
Mifmif

Un'altra alternativa potrebbe essere questa
Vladislav Varslavans

Risposte:


40

Modifica: come menzionato nei commenti, c'è una libreria disponibile su Google Code per ottenere questo: https://code.google.com/archive/p/xeger/

Vedi anche https://github.com/mifmif/Generex come suggerito da Mifmif

Messaggio originale:

In primo luogo, con un'espressione regolare abbastanza complessa, credo che questo possa essere impossibile. Ma dovresti essere in grado di mettere insieme qualcosa per semplici espressioni regolari.

Se dai un'occhiata al codice sorgente della classe java.util.regex.Pattern, vedrai che utilizza una rappresentazione interna delle istanze di Node. Ciascuno dei diversi componenti del pattern ha la propria implementazione di una sottoclasse Node. Questi nodi sono organizzati in un albero.

Producendo un visitatore che attraversa questo albero, dovresti essere in grado di chiamare un metodo del generatore sovraccarico o un qualche tipo di Builder che mette insieme qualcosa.


2
Non sono sicuro che Xeger sia così bravo. Non può gestire classi di caratteri. Non riesce a riconoscere un semplice [\w]. Uno sguardo all'ultima riga del loro wiki ce lo dice.
John Red

2
Nota anche che questi dipendono da dk.brics.automatonquindi preparati ad aggiungere dipendenze pom di terze parti. Alla maggior parte delle persone non importa, ma vorrei che ci fosse qualcosa di un po 'più compatto.
Sridhar Sarnobat

C'è un'alternativa per xeger e generex. Manca di tutti questi inconvenienti e non è obsoleto. Per favore scorri verso il basso fino alla mia risposta.
Vladislav Varslavans il

"In primo luogo, con un'espressione regolare abbastanza complessa, credo che questo possa essere impossibile." - questo non è strettamente vero : qualsiasi regex che passa contro qualcosa può anche generare un input valido. Spiegazione: le espressioni regolari sono di tipo 3 nella gerarchia di Chomsky, il che significa che possono essere espresse come FSM. Quando si passa attraverso un FSM, ogni fronte viene interpretato come una regola per il carattere successivo, quindi un FSM può essere utilizzato per analizzare o generare sequenze. Se un FSM ha un percorso per il terminale, è possibile determinare una sequenza valida. Quindi, è "impossibile" solo se non c'è un percorso per il terminale (che sarebbe una regex inutile).
Lawrence Wagerfield

22

È troppo tardi per aiutare il poster originale, ma potrebbe aiutare un nuovo arrivato. Generex è un'utile libreria java che fornisce molte funzionalità per l'utilizzo di regex per generare stringhe (generazione casuale, generazione di una stringa basata sul suo indice, generazione di tutte le stringhe ...).

Esempio :

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

Divulgazione

Il progetto menzionato in questo post appartiene all'utente che risponde (Mifmif) alla domanda. Secondo le regole , questo deve essere sollevato.


11
Sembra che Generex sia un tuo progetto. Ti dispiacerebbe menzionare nel tuo post che questo è il tuo progetto, come da regole qui ?
Brian McCutchon


5

Sono andato alla radice di laminazione mia propria libreria per che (in C #, ma dovrebbe essere facile da capire per uno sviluppatore Java).

Rxrdg è nato come soluzione a un problema di creazione di dati di test per un progetto di vita reale. L'idea di base è sfruttare i modelli di convalida esistenti (espressioni regolari) per creare dati casuali che siano conformi a tali modelli. In questo modo vengono creati dati casuali validi.

Non è così difficile scrivere un parser per semplici schemi regex. Usare un albero di sintassi astratto per generare stringhe dovrebbe essere ancora più semplice.


link non punta più al repository. Vorrei andare con openhub.net/p/rxrdg . La soluzione non si costruisce, però?
Veverke

4

Sul podcast stackoverflow 11:

Spolsky: Sì. C'è anche un nuovo prodotto, se non vuoi usare il Team System, i nostri amici di Redgate hanno un prodotto chiamato SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . È $ 295 e genera solo alcuni dati di test realistici. E fa cose come generare effettivamente città reali nella colonna della città che esistono realmente, e poi quando genera quelle otterrà lo stato giusto, invece di sbagliare lo stato, o inserire stati nelle città tedesche e cose come ... sai, genera dati dall'aspetto piuttosto realistico. Non sono proprio sicuro di quali siano tutte le caratteristiche.

Questo probabilmente non è quello che stai cercando, ma potrebbe essere un buon punto di partenza, invece di crearne uno tuo.

Non riesco a trovare nulla in Google, quindi suggerirei di affrontare il problema analizzando una data espressione regolare nelle più piccole unità di lavoro (\ w, [xx], \ d, ecc.) E scrivendo alcuni metodi di base per supportare quelle frasi di espressione regolare.

Quindi per \ w avresti un metodo getRandomLetter () che restituisce qualsiasi lettera casuale, e avresti anche getRandomLetter (char startLetter, char endLetter) che ti dà una lettera casuale tra i due valori.


4

Questa domanda è davvero vecchia, anche se il problema era reale per me. Ho provato xeger e Generex e non sembrano soddisfare le mie esigenze. In realtà non riescono a elaborare alcuni dei modelli regex (come a{60000}) o per altri (ad esempio (A|B|C|D|E|F)) semplicemente non producono tutti i valori possibili. Dal momento che non ho trovato un'altra soluzione appropriata, ho creato la mia libreria.

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

Sono disponibili anche artefatti su Maven centrale.

Esempio di utilizzo:

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

3

So che c'è già una risposta accettata, ma ho usato Data Generator di RedGate (quello menzionato nella risposta di Craig) e funziona DAVVERO bene per tutto ciò che ho lanciato. È veloce e questo mi lascia desiderare di usare la stessa regex per generare i dati reali per cose come i codici di registrazione che questa cosa sputa.

Ci vuole una regex come:

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

e genera tonnellate di codici univoci come:

LLK-32U

È un grande algoritmo segreto che RedGate ha scoperto e siamo tutti sfortunati o è qualcosa che noi comuni mortali potremmo davvero fare?


3

Sono in volo e ho appena visto la domanda: ho scritto la soluzione più semplice ma inefficiente e incompleta. Spero che possa aiutarti a iniziare a scrivere il tuo parser:

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

Potresti voler indicare che tipo di stringhe vengono usate come input del pattern. Prima di tutto, non è così facile determinare queste cose dal codice sorgente. In secondo luogo, se ci sono errori o non chiarezza nel codice sorgente, non c'è modo di vedere se sono intenzionali o meno.
Maarten Bodewes

StringTokenizer è una classe legacy che viene mantenuta per motivi di compatibilità sebbene il suo utilizzo sia sconsigliato nel nuovo codice. Si consiglia a chiunque cerchi questa funzionalità di utilizzare il metodo split di String o il pacchetto java.util.regex.
Rohit

2

Dovrai scrivere il tuo parser, come ha fatto l'autore di String :: Random (Perl). In effetti, non usa regex da nessuna parte in quel modulo, è proprio quello a cui sono abituati i programmatori perl.

D'altra parte, forse puoi dare un'occhiata alla fonte , per ottenere alcuni suggerimenti.


EDIT: Dannazione, Blair mi ha battuto sul tempo di 15 secondi.


1

È lontano dal supportare un'espressione regolare PCRE completa, ma ho scritto il seguente metodo Ruby per prendere una stringa simile a un'espressione regolare e produrre una variazione su di essa. (Per CAPTCHA basato sulla lingua.)

# 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


0

Se desideri generare stringhe "critiche", potresti prendere in considerazione:

EGRET http://elarson.pythonanywhere.com/ che genera stringhe "malvagie" che coprono le tue espressioni regolari

MUTREX http://cs.unibg.it/mutrex/ che genera stringhe di rilevamento dei guasti mediante mutazione regex

Entrambi sono strumenti accademici (io sono uno degli autori di quest'ultimo) e funzionano abbastanza bene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.