Utiliser Regex pour générer des chaînes au lieu de les faire correspondre


109

J'écris un utilitaire Java qui m'aide à générer des tonnes de données pour les tests de performances. Ce serait vraiment cool de pouvoir spécifier une regex pour Strings afin que mon générateur crache des choses qui correspondent à cela. Y a-t-il quelque chose de déjà cuit que je peux utiliser pour le faire? Ou y a-t-il une bibliothèque qui m'accompagne la plupart du temps?

Merci


1
Voici une bibliothèque java utile qui fournit de nombreuses fonctionnalités pour utiliser regex pour générer une chaîne (génération aléatoire, générer une chaîne en fonction de son index, générer toute la chaîne ..) Découvrez-la ici
Mifmif

Une autre alternative pourrait être celle-ci
Vladislav Varslavans

Réponses:


40

Edit: Comme mentionné dans les commentaires, il existe une bibliothèque disponible sur Google Code pour y parvenir: https://code.google.com/archive/p/xeger/

Voir aussi https://github.com/mifmif/Generex comme suggéré par Mifmif

Message d'origine:

Premièrement, avec une expression rationnelle suffisamment complexe, je pense que cela peut être impossible. Mais vous devriez pouvoir créer quelque chose pour des expressions rationnelles simples.

Si vous regardez le code source de la classe java.util.regex.Pattern, vous verrez qu'elle utilise une représentation interne des instances de Node. Chacun des différents composants de modèle a sa propre implémentation d'une sous-classe Node. Ces nœuds sont organisés dans un arbre.

En produisant un visiteur qui parcourt cet arbre, vous devriez pouvoir appeler une méthode de générateur surchargée ou une sorte de Builder qui concocte quelque chose ensemble.


2
Je ne suis pas sûr que Xeger soit aussi bon. Il ne peut pas gérer les classes de caractères. Il ne parvient pas à reconnaître un simple [\w]. Un regard sur la dernière ligne de leur wiki nous le dit.
John Red

2
Notez également que ceux-ci dépendent dk.brics.automatondonc soyez prêt à ajouter des dépendances pom tierces. La plupart des gens ne s'en soucient pas, mais j'aimerais qu'il y ait quelque chose d'un peu plus compact.
Sridhar Sarnobat

Il existe une alternative pour xeger et generex. Il manque tous ces inconvénients et n'est pas obsolète. Veuillez faire défiler jusqu'à ma réponse.
Vladislav Varslavans le

"Premièrement, avec une expression régulière suffisamment complexe, je pense que cela peut être impossible." - ce n'est pas strictement vrai : toute regex qui passe contre quelque chose peut également générer une entrée valide. Explication: les expressions régulières sont de type 3 sur la hiérarchie Chomsky, ce qui signifie qu'elles peuvent être exprimées en FSM. Lorsque vous parcourez un FSM, chaque arête est interprétée comme une règle pour le caractère suivant, ainsi un FSM peut être utilisé pour analyser ou générer des séquences. Si un FSM a un chemin vers le terminal, une séquence valide peut être déterminée. Donc, ce n'est "impossible" que s'il n'y a pas de chemin vers le terminal (ce qui serait un regex inutile).
Lawrence Wagerfield le

22

Il est trop tard pour aider l'affiche originale, mais cela pourrait aider un nouveau venu. Generex est une bibliothèque java utile qui fournit de nombreuses fonctionnalités pour utiliser des expressions rationnelles pour générer des chaînes (génération aléatoire, génération d'une chaîne basée sur son index, génération de toutes les chaînes ...).

Exemple :

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

Divulgation

Le projet mentionné sur ce post appartient à l'utilisateur répondant (Mifmif) à la question. Conformément aux règles , cela doit être évoqué.


11
Il semble que Generex soit votre propre projet. Pourriez-vous mentionner dans votre message qu'il s'agit de votre propre projet, conformément aux règles ici ?
Brian McCutchon

20

Xeger (Java) est également capable de le faire:

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

1
Xeger fonctionne bien. MAIS assurez-vous que vous avez le pot de l' automate sur le chemin de la classe ou dans votre pom / gradle
Delicia Brummitt

5

J'ai commencé à rouler moi- même bibliothèque pour cela (en c # mais devrait être facile à comprendre pour un développeur Java).

Rxrdg a commencé comme une solution à un problème de création de données de test pour un projet réel. L'idée de base est de tirer parti des modèles de validation existants (expression régulière) pour créer des données aléatoires conformes à ces modèles. De cette façon, des données aléatoires valides sont créées.

Il n'est pas si difficile d'écrire un analyseur pour des modèles de regex simples. L'utilisation d'un arbre de syntaxe abstraite pour générer des chaînes devrait être encore plus simple.


le lien ne pointe plus vers le référentiel. J'irais avec openhub.net/p/rxrdg . La solution ne construit pas, cependant?
Veverke

4

Sur le podcast stackoverflow 11:

Spolsky: Oui. Il existe également un nouveau produit, si vous ne souhaitez pas utiliser Team System, nos amis de Redgate ont un produit appelé SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . C'est 295 $ et cela génère juste des données de test réalistes. Et il fait des choses comme générer de vraies villes dans la colonne de la ville qui existent réellement, et puis quand il les génère, il obtiendra l'état correct, au lieu de se tromper sur l'état, ou de mettre des États dans des villes allemandes et des trucs comme ... vous savez, cela génère des données assez réalistes. Je ne sais pas vraiment quelles sont toutes les fonctionnalités.

Ce n'est probablement pas ce que vous recherchez, mais cela pourrait être un bon point de départ, au lieu de créer le vôtre.

Je ne semble pas trouver quoi que ce soit dans google, donc je suggérerais de résoudre le problème en analysant une expression régulière donnée dans les plus petites unités de travail (\ w, [xx], \ d, etc.) et en écrivant quelques méthodes de base pour prendre en charge ces expressions d'expressions régulières.

Donc pour \ w vous auriez une méthode getRandomLetter () qui renvoie n'importe quelle lettre aléatoire, et vous auriez également getRandomLetter (char startLetter, char endLetter) qui vous donne une lettre aléatoire entre les deux valeurs.


4

Cette question est vraiment ancienne, même si le problème était réel pour moi. J'ai essayé xeger et Generex et ils ne semblent pas répondre à mes exigences. En fait, ils ne parviennent pas à traiter certains des modèles de regex (comme a{60000}) ou pour d'autres (par exemple (A|B|C|D|E|F)), ils ne produisent tout simplement pas toutes les valeurs possibles. Comme je n'ai trouvé aucune autre solution appropriée, j'ai créé ma propre bibliothèque.

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

Il y a aussi un artefact sur maven central disponible.

Exemple d'utilisation:

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

3

Je sais qu'il y a déjà une réponse acceptée, mais j'ai utilisé le générateur de données de RedGate (celui mentionné dans la réponse de Craig) et cela fonctionne VRAIMENT bien pour tout ce que j'ai lancé. C'est rapide et cela me laisse envie d'utiliser la même expression régulière pour générer les données réelles pour des choses comme les codes d'enregistrement que cette chose crache.

Il faut une expression régulière comme:

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

et il génère des tonnes de codes uniques comme:

LLK-32U

Est-ce un gros algorithme secret que RedGate a découvert et que nous n'avons tous pas de chance ou est-ce quelque chose que nous, simples mortels, pourrions faire?


3

Je suis en vol et je viens de voir la question: j'ai écrit la solution la plus simple mais inefficace et incomplète. J'espère que cela vous aidera à commencer à écrire votre propre analyseur:

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

Vous souhaiterez peut-être indiquer le type de chaînes utilisées comme entrée de modèle. Tout d'abord, il n'est pas si simple de déterminer de telles choses à partir du code source. Deuxièmement, s'il y a des erreurs ou des incohérences dans le code source, il n'y a aucun moyen de voir si elles sont intentionnelles ou non.
Maarten Bodewes

StringTokenizer est une classe héritée qui est conservée pour des raisons de compatibilité bien que son utilisation soit déconseillée dans le nouveau code. Il est recommandé à quiconque recherche cette fonctionnalité d'utiliser à la place la méthode split de String ou le package java.util.regex.
Rohit

2

Vous devrez écrire votre propre analyseur, comme l'a fait l'auteur de String :: Random (Perl). En fait, il n'utilise les expressions régulières nulle part dans ce module, c'est juste ce à quoi les codeurs perl sont habitués.

D'un autre côté, vous pouvez peut-être jeter un coup d'œil à la source , pour obtenir des indications.


EDIT: Merde, blair m'a battu au coup de poing de 15 secondes.


1

Il est loin de supporter une expression régulière PCRE complète, mais j'ai écrit la méthode Ruby suivante pour prendre une chaîne de type regexp et en produire une variation. (Pour CAPTCHA basé sur la langue.)

# 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

Cette question est très ancienne, mais je suis tombée dessus lors de ma propre recherche, je vais donc inclure quelques liens pour d'autres personnes qui pourraient rechercher la même fonctionnalité dans d'autres langues.


0

Si vous souhaitez générer des chaînes "critiques", vous pouvez envisager:

EGRET http://elarson.pythonanywhere.com/ qui génère des chaînes "diaboliques" couvrant vos expressions régulières

MUTREX http://cs.unibg.it/mutrex/ qui génère des chaînes de détection de pannes par mutation regex

Les deux sont des outils académiques (je suis l'un des auteurs de ce dernier) et fonctionnent raisonnablement bien.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.