Usando Regex para gerar Strings em vez de combiná-los

108

Estou escrevendo um utilitário Java que me ajuda a gerar muitos dados para testes de desempenho. Seria muito legal poder especificar uma regex para Strings de modo que meu gerador gerasse coisas que correspondessem a isso. Existe algo lá fora que eu possa usar para fazer isso? Ou existe uma biblioteca que me leva quase todo o caminho até lá?

obrigado

Andrew Harmel-Law
fonte
1
Aqui está uma biblioteca Java útil que fornece muitos recursos para usar regex para gerar String (geração aleatória, gerar String com base em seu índice, gerar todas as String ..) confira aqui
Mifmif
Outra alternativa poderia ser esta
Vladislav Varslavans

Respostas:

40

Editar: conforme mencionado nos comentários, há uma biblioteca disponível no Google Code para fazer isso: https://code.google.com/archive/p/xeger/

Veja também https://github.com/mifmif/Generex conforme sugerido por Mifmif

Mensagem original:

Em primeiro lugar, com uma regexp complexa o suficiente, acredito que isso pode ser impossível. Mas você deve ser capaz de criar algo para expressões regulares simples.

Se você der uma olhada no código-fonte da classe java.util.regex.Pattern, verá que ele usa uma representação interna das instâncias do Node. Cada um dos diferentes componentes do padrão tem sua própria implementação de uma subclasse de nó. Esses nós são organizados em uma árvore.

Ao produzir um visitante que atravessa essa árvore, você deve ser capaz de chamar um método gerador sobrecarregado ou algum tipo de Builder que junta algo.

Cheekysoft
fonte
2
Não tenho certeza se Xeger é tão bom. Ele não pode lidar com classes de personagens. Ele não consegue reconhecer um simples [\w]. Uma olhada na última linha de seu wiki nos diz isso.
John Red
2
Observe também que eles dependem, dk.brics.automatonportanto, esteja preparado para adicionar dependências de pom de terceiros. A maioria das pessoas não se importa com isso, mas gostaria que houvesse algo um pouco mais compacto.
Sridhar Sarnobat
Existe uma alternativa para xeger e generex. Não possui todas essas desvantagens e não é obsoleto. Por favor, role para baixo até minha resposta.
Vladislav Varslavans
"Em primeiro lugar, com uma regexp complexa o suficiente, acredito que isso pode ser impossível." - isso não é estritamente verdadeiro : qualquer regex que passa em relação a algo também pode gerar uma entrada válida. Explicação: os regexes são do tipo 3 na Hierarquia de Chomsky, o que significa que podem ser expressos como FSMs. Ao percorrer um FSM, cada borda é interpretada como uma regra para o próximo caractere, portanto, um FSM pode ser usado para analisar ou gerar sequências. Se um FSM tem um caminho para o terminal, uma sequência válida pode ser determinada. Portanto, só é "impossível" se não houver caminho para o terminal (o que seria um regex inútil).
Lawrence Wagerfield
22

É tarde demais para ajudar o autor da postagem original, mas pode ajudar um recém-chegado. Generex é uma biblioteca Java útil que fornece muitos recursos para usar regexes para gerar strings (geração aleatória, gerando uma string com base em seu índice, gerando todas as strings ...).

Exemplo:

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

Divulgação

O projeto mencionado neste post pertence ao usuário que respondeu (Mifmif) à pergunta. De acordo com as regras , isso precisa ser mencionado.

Mifmif
fonte
11
Parece que Generex é seu próprio projeto. Você se importaria de mencionar em seu post que este é seu próprio projeto, conforme as regras aqui ?
Brian McCutchon
20

Xeger (Java) também é capaz de fazer isso:

String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
String result = generator.generate();
assert result.matches(regex);
Wilfred Springer
fonte
1
O Xeger funciona bem. MAS certifique-se de que você tem o frasco do autômato no caminho da aula ou em seu pom / gradle
Delicia Brummitt
5

Eu fui a raiz de lançar minha própria biblioteca para isso (em c #, mas deve ser fácil de entender para um desenvolvedor Java).

Rxrdg começou como uma solução para um problema de criação de dados de teste para um projeto da vida real. A ideia básica é aproveitar os padrões de validação existentes (expressão regular) para criar dados aleatórios que estejam em conformidade com esses padrões. Desta forma, dados aleatórios válidos são criados.

Não é tão difícil escrever um analisador para padrões regex simples. Usar uma árvore de sintaxe abstrata para gerar strings deve ser ainda mais fácil.

Goran
fonte
o link não aponta mais para o repositório. Eu iria com openhub.net/p/rxrdg . A solução não constrói, porém?
Veverke
4

No podcast stackoverflow 11:

Spolsky: Sim. Há um novo produto também, se você não quiser usar o Team System, nossos amigos da Redgate têm um produto chamado SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] . É $ 295 e apenas gera alguns dados de teste realistas. E ele faz coisas como realmente gerar cidades reais na coluna da cidade que realmente existem, e então, quando as gerar, ele obterá o estado certo, em vez de errar o estado, ou colocar estados em cidades alemãs e coisas assim ... você sabe, ele gera dados de aparência bastante realistas. Não tenho certeza de quais são todos os recursos.

Provavelmente não é o que você está procurando, mas pode ser um bom ponto de partida, em vez de criar o seu próprio.

Não consigo encontrar nada no google, então sugiro resolver o problema analisando uma determinada expressão regular nas menores unidades de trabalho (\ w, [xx], \ d, etc) e escrevendo alguns métodos básicos para oferecer suporte aquelas frases de expressão regular.

Portanto, para \ w, você teria um método getRandomLetter () que retorna qualquer letra aleatória e também getRandomLetter (char startLetter, char endLetter) que fornece uma letra aleatória entre os dois valores.

Craig
fonte
4

Esta pergunta é muito antiga, embora o problema fosse real para mim. Eu tentei xeger e Generex e eles não parecem atender às minhas necessidades. Eles realmente falham em processar alguns dos padrões regex (como a{60000}) ou para outros (por exemplo (A|B|C|D|E|F)) eles simplesmente não produzem todos os valores possíveis. Como não encontrei nenhuma outra solução apropriada, criei minha própria biblioteca.

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

Também há artefato disponível na central maven.

Exemplo de uso:

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

Eu sei que já existe uma resposta aceita, mas estou usando o Data Generator da RedGate (o que foi mencionado na resposta de Craig) e funciona MUITO bem para tudo que lancei nele. É rápido e me deixa com vontade de usar a mesma regex para gerar os dados reais para coisas como códigos de registro que essa coisa gera.

É necessário um regex como:

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

e gera toneladas de códigos exclusivos como:

LLK-32U

É algum grande algoritmo secreto que RedGate descobriu e estamos todos sem sorte ou é algo que nós, meros mortais, realmente podemos fazer?

J Wynia
fonte
3

Estou voando e acabei de ver a pergunta: escrevi a solução mais fácil, mas ineficiente e incompleta. Espero que ajude você a começar a escrever seu próprio analisador:

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
fonte
Você pode querer indicar que tipo de strings são usadas como entrada de padrão. Em primeiro lugar, não é tão fácil determinar essas coisas a partir do código-fonte. Em segundo lugar, se houver algum erro ou falta de clareza no código-fonte, não há como saber se foi intencional ou não.
Maarten Bodewes
StringTokenizer é uma classe legada mantida por motivos de compatibilidade, embora seu uso seja desencorajado no novo código. Recomenda-se que qualquer pessoa buscando essa funcionalidade use o método de divisão de String ou o pacote java.util.regex.
Rohit
2

Você terá que escrever seu próprio analisador, como o autor de String :: Random (Perl) fez. Na verdade, ele não usa regexes em nenhum lugar daquele módulo, é apenas o que os perl-coders estão acostumados.

Por outro lado, talvez você possa dar uma olhada na fonte para obter algumas dicas.


EDIT: Droga, Blair me venceu por 15 segundos.

Espo
fonte
1

Está longe de oferecer suporte a uma expressão regular PCRE completa, mas escrevi o seguinte método Ruby para pegar uma string semelhante a regexp e produzir uma variação nela. (Para CAPTCHA baseado em 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

fonte
0

Se você deseja gerar strings "críticas", pode considerar:

EGRET http://elarson.pythonanywhere.com/ que gera strings "malignas" cobrindo suas expressões regulares

MUTREX http://cs.unibg.it/mutrex/ que gera strings de detecção de falhas por mutação regex

Ambos são ferramentas acadêmicas (sou um dos autores deste último) e funcionam razoavelmente bem.

Angelo Gargantini
fonte