Verwenden von Regex, um Zeichenfolgen zu generieren, anstatt sie abzugleichen

107

Ich schreibe ein Java-Dienstprogramm, mit dem ich viele Daten für Leistungstests generieren kann. Es wäre wirklich cool, einen regulären Ausdruck für Strings angeben zu können, damit mein Generator Dinge ausspuckt, die dazu passen. Gibt es da draußen schon etwas Gebackenes, mit dem ich das machen kann? Oder gibt es eine Bibliothek, die mich den größten Teil des Weges dorthin bringt?

Vielen Dank

Andrew Harmel-Law
quelle
1
Hier ist eine nützliche Java-Bibliothek , die viele Funktionen für die Verwendung von Regex zum Generieren von Strings bietet (zufällige Generierung, Generieren von Strings basierend auf dem Index, Generieren aller
Strings
Eine andere Alternative könnte dies sein
Vladislav Varslavans

Antworten:

40

Bearbeiten: Wie in den Kommentaren erwähnt, steht bei Google Code eine Bibliothek zur Verfügung, um dies zu erreichen: https://code.google.com/archive/p/xeger/

Siehe auch https://github.com/mifmif/Generex, wie von Mifmif vorgeschlagen

Originale Nachricht:

Erstens glaube ich, dass dies mit einem ausreichend komplexen regulären Ausdruck unmöglich sein kann. Aber Sie sollten in der Lage sein, etwas für einfache reguläre Ausdrücke zusammenzustellen.

Wenn Sie sich den Quellcode der Klasse java.util.regex.Pattern ansehen, werden Sie feststellen, dass eine interne Darstellung von Knoteninstanzen verwendet wird. Jede der verschiedenen Musterkomponenten hat ihre eigene Implementierung einer Knotenunterklasse. Diese Knoten sind in einem Baum organisiert.

Wenn Sie einen Besucher erstellen, der diesen Baum durchquert, sollten Sie in der Lage sein, eine überladene Generatormethode oder eine Art Builder aufzurufen, der etwas zusammenschustert.

Cheekysoft
quelle
2
Ich bin mir nicht sicher, ob Xeger so gut ist. Es kann keine Zeichenklassen verarbeiten. Es erkennt ein einfaches nicht [\w]. Ein Blick in die letzte Zeile ihres Wikis sagt uns das.
John Red
2
Beachten Sie auch, dass diese davon abhängen. Seien Sie dk.brics.automatonalso darauf vorbereitet, POM-Abhängigkeiten von Drittanbietern hinzuzufügen. Die meisten Leute haben nichts dagegen, aber ich wünschte, es gäbe etwas Kompakteres.
Sridhar Sarnobat
Es gibt eine Alternative für Xeger und Generex. Es fehlen all diese Nachteile und es ist nicht veraltet. Bitte scrollen Sie nach unten zu meiner Antwort.
Vladislav Varslavans
"Erstens glaube ich, dass dies mit einem ausreichend komplexen regulären Ausdruck unmöglich sein kann." - Dies ist nicht unbedingt der Fall : Jeder reguläre Ausdruck, der gegen etwas weitergegeben wird, kann auch eine gültige Eingabe generieren. Erläuterung: Regexe sind in der Chomsky-Hierarchie vom Typ 3, dh sie können als FSMs ausgedrückt werden. Beim Durchlaufen eines FSM wird jede Kante als Regel für das nächste Zeichen interpretiert, sodass ein FSM zum Parsen oder Generieren von Sequenzen verwendet werden kann. Wenn ein FSM einen Pfad zum Terminal hat, kann eine gültige Sequenz bestimmt werden. Es ist also nur "unmöglich", wenn es keinen Pfad zum Terminal gibt (was eine nutzlose Regex wäre).
Lawrence Wagerfield
22

Es ist zu spät, um dem Originalplakat zu helfen, aber es könnte einem Neuling helfen. Generex ist eine nützliche Java-Bibliothek, die viele Funktionen für die Verwendung von regulären Ausdrücken zum Generieren von Zeichenfolgen bietet (zufällige Generierung, Generierung einer Zeichenfolge basierend auf ihrem Index, Generierung aller Zeichenfolgen ...).

Beispiel:

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

Offenlegung

Das in diesem Beitrag erwähnte Projekt gehört dem Benutzer, der die Frage beantwortet (Mifmif). Gemäß den Regeln muss dies zur Sprache gebracht werden.

Mifmif
quelle
11
Es sieht so aus, als wäre Generex Ihr eigenes Projekt. Würde es Ihnen etwas ausmachen, in Ihrem Beitrag zu erwähnen, dass dies Ihr eigenes Projekt ist, gemäß den Regeln hier ?
Brian McCutchon
20

Xeger (Java) kann das auch:

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);
Wilfred Springer
quelle
1
Xeger funktioniert gut. ABER stellen Sie sicher, dass Sie das Automatenglas auf dem Klassenpfad oder in Ihrem Pom / Gradle haben
Delicia Brummitt
5

Dafür habe ich meine eigene Bibliothek ins Rollen gebracht (In c #, sollte aber für einen Java-Entwickler leicht verständlich sein).

Rxrdg begann als Lösung für ein Problem beim Erstellen von Testdaten für ein reales Projekt. Die Grundidee besteht darin, die vorhandenen Validierungsmuster (reguläre Ausdrücke) zu nutzen, um zufällige Daten zu erstellen, die solchen Mustern entsprechen. Auf diese Weise werden gültige Zufallsdaten erstellt.

Es ist nicht so schwierig, einen Parser für einfache Regex-Muster zu schreiben. Die Verwendung eines abstrakten Syntaxbaums zum Generieren von Zeichenfolgen sollte noch einfacher sein.

Goran
quelle
Der Link verweist nicht mehr auf das Repository. Ich würde mit openhub.net/p/rxrdg gehen . Die Lösung baut sich aber nicht auf?
Veverke
4

Beim Stackoverflow-Podcast 11:

Spolsky: Ja. Es gibt auch ein neues Produkt, wenn Sie das Team-System dort nicht verwenden möchten. Unsere Freunde bei Redgate haben ein Produkt namens SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm]. . Es kostet 295 US-Dollar und generiert nur einige realistische Testdaten. Und es macht Dinge wie das Erzeugen realer Städte in der tatsächlich existierenden Stadtspalte, und wenn es dann diese erzeugt, wird der Staat richtig gemacht, anstatt den Staat falsch zu machen, oder Staaten in deutsche Städte zu bringen und so ... Sie wissen, es generiert ziemlich realistisch aussehende Daten. Ich bin mir nicht sicher, was alle Funktionen sind.

Dies ist wahrscheinlich nicht das, wonach Sie suchen, aber es könnte ein guter Ausgangspunkt sein, anstatt Ihren eigenen zu erstellen.

Ich kann anscheinend nichts in Google finden, daher würde ich vorschlagen, das Problem zu lösen, indem ich einen bestimmten regulären Ausdruck in die kleinsten Arbeitseinheiten (\ w, [xx], \ d usw.) zerlege und einige grundlegende Methoden zur Unterstützung schreibe diese Sätze mit regulären Ausdrücken.

Für \ w hätten Sie also eine Methode getRandomLetter (), die einen beliebigen zufälligen Buchstaben zurückgibt, und Sie hätten auch getRandomLetter (char startLetter, char endLetter), die Ihnen einen zufälligen Buchstaben zwischen den beiden Werten gibt.

Craig
quelle
3

Ich weiß, dass es bereits eine akzeptierte Antwort gibt, aber ich habe den Datengenerator von RedGate (den in Craigs Antwort erwähnten) verwendet und er funktioniert WIRKLICH gut für alles, was ich darauf geworfen habe. Es ist schnell und das lässt mich den gleichen regulären Ausdruck verwenden, um die realen Daten für Dinge wie Registrierungscodes zu generieren, die dieses Ding ausspuckt.

Es braucht einen regulären Ausdruck wie:

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

und es generiert Tonnen von eindeutigen Codes wie:

LLK-32U

Ist dies ein großer geheimer Algorithmus, den RedGate herausgefunden hat und bei dem wir alle kein Glück haben, oder ist es etwas, was wir Sterblichen tatsächlich tun könnten?

J Wynia
quelle
3

Ich bin auf dem Flug und habe gerade die Frage gesehen: Ich habe die einfachste, aber ineffiziente und unvollständige Lösung geschrieben. Ich hoffe, es kann Ihnen helfen, Ihren eigenen Parser zu schreiben:

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
quelle
Möglicherweise möchten Sie angeben, welche Art von Zeichenfolgen als Mustereingabe verwendet werden. Erstens ist es gar nicht so einfach, solche Dinge aus dem Quellcode zu bestimmen. Zweitens, wenn der Quellcode Fehler oder Unklarheiten enthält, kann nicht festgestellt werden, ob diese beabsichtigt sind oder nicht.
Maarten Bodewes
StringTokenizer ist eine Legacy-Klasse, die aus Kompatibilitätsgründen beibehalten wird, obwohl von ihrer Verwendung in neuem Code abgeraten wird. Es wird empfohlen, dass jeder, der diese Funktionalität sucht, stattdessen die Split-Methode von String oder das Paket java.util.regex verwendet.
Rohit
3

Diese Frage ist wirklich alt, obwohl das Problem für mich tatsächlich war. Ich habe xeger und Generex ausprobiert und sie scheinen meine Anforderungen nicht zu erfüllen. Sie verarbeiten tatsächlich einige der Regex-Muster (wie a{60000}) nicht oder für andere (z. B. (A|B|C|D|E|F)) erzeugen sie einfach nicht alle möglichen Werte. Da ich keine andere geeignete Lösung gefunden habe, habe ich meine eigene Bibliothek erstellt.

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

Es gibt auch Artefakte auf Maven Central zur Verfügung.

Anwendungsbeispiel:

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

Sie müssen Ihren eigenen Parser schreiben, wie es der Autor von String :: Random (Perl) getan hat. Tatsächlich verwendet er nirgendwo in diesem Modul Regexe, sondern genau das, was Perl-Codierer gewohnt sind.

Auf der anderen Seite können Sie sich vielleicht die Quelle ansehen , um einige Hinweise zu erhalten.


EDIT: Verdammt, Blair hat mich um 15 Sekunden geschlagen.

Espo
quelle
1

Es ist weit davon entfernt, einen vollständigen PCRE-Regexp zu unterstützen, aber ich habe die folgende Ruby-Methode geschrieben, um einen Regexp-ähnlichen String zu verwenden und eine Variation davon zu erstellen. (Für sprachbasiertes 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

quelle
1

Diese Frage ist sehr alt, aber ich bin bei meiner eigenen Suche darauf gestoßen, daher werde ich ein paar Links für andere einfügen, die möglicherweise nach derselben Funktionalität in anderen Sprachen suchen.

Everett
quelle
0

Wenn Sie "kritische" Zeichenfolgen generieren möchten, sollten Sie Folgendes berücksichtigen:

EGRET http://elarson.pythonanywhere.com/ , das "böse" Zeichenfolgen generiert, die Ihre regulären Ausdrücke abdecken

MUTREX http://cs.unibg.it/mutrex/ , das durch Regex-Mutation Fehlererkennungszeichenfolgen generiert

Beide sind akademische Werkzeuge (ich bin einer der Autoren des letzteren) und funktionieren ziemlich gut.

Angelo Gargantini
quelle