简体   繁体   English

使用正则表达式生成字符串而不是匹配它们

[英]Using Regex to generate Strings rather than match them

I am writing a Java utility which helps me to generate loads of data for performance testing.我正在编写一个 Java 实用程序,它可以帮助我生成大量数据以进行性能测试。 It would be really cool to be able to specify a regex for Strings so that my generator spits out things which match this.能够为字符串指定一个正则表达式,这样我的生成器会吐出与此匹配的东西,这真是太酷了。 Is there something out there already baked which I can use to do this?有什么东西已经烤好了我可以用来做这个吗? Or is there a library which gets me most of the way there?还是有一个图书馆可以让我大部分时间到达那里?

Thanks谢谢

Edit:编辑:

Complete list of suggested libraries on this question:关于这个问题的建议库的完整列表:

  1. Xeger * - Java Xeger * - Java
  2. Generex * - Java Generex * - Java
  3. Rgxgen - Java Rgxgen -Java
  4. rxrdg - C# rxrdg - C#

* - Depends on dk.brics.automaton * - 取决于dk.brics.automaton

Edit: As mentioned in the comments, there is a library available at Google Code to achieve this: https://code.google.com/archive/p/xeger/编辑:正如评论中提到的,谷歌代码中有一个库可以实现这一点: https ://code.google.com/archive/p/xeger/

See also https://github.com/mifmif/Generex as suggested by Mifmif另请参阅Mifmif建议的https://github.com/mifmif/Generex

Original message:原始信息:

Firstly, with a complex enough regexp, I believe this can be impossible.首先,使用足够复杂的正则表达式,我相信这是不可能的。 But you should be able to put something together for simple regexps.但是你应该能够为简单的正则表达式组合一些东西。

If you take a look at the source code of the class java.util.regex.Pattern, you'll see that it uses an internal representation of Node instances.如果您查看类 java.util.regex.Pattern 的源代码,您会发现它使用 Node 实例的内部表示。 Each of the different pattern components have their own implementation of a Node subclass.每个不同的模式组件都有自己的 Node 子类实现。 These Nodes are organised into a tree.这些节点被组织成一棵树。

By producing a visitor that traverses this tree, you should be able to call an overloaded generator method or some kind of Builder that cobbles something together.通过生成遍历这棵树的访问者,您应该能够调用重载的生成器方法或某种将某些东西拼凑在一起的 Builder。

It's too late to help the original poster, but it could help a newcomer.帮助原始海报为时已晚,但它可以帮助新人。 Generex is a useful java library that provides many features for using regexes to generate strings (random generation, generating a string based on its index, generating all strings...). Generex是一个有用的 java 库,它提供了许多使用正则表达式生成字符串的功能(随机生成,根据其索引生成字符串,生成所有字符串......)。

Example :例子 :

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

Disclosure披露

The project mentioned on this post belongs to the user answering (Mifmif) the question.这篇文章中提到的项目属于回答(Mifmif)问题的用户。 As per the rules , this need to be brought up.根据规则,这需要提出。

Xeger (Java) is capable of doing it as well: Xeger (Java)也能够做到这一点:

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

This question is really old, though the problem was actual for me.这个问题真的很老,虽然这个问题对我来说是真实的。 I've tried xeger and Generex and they doesn't seem to meet my reguirements.我试过xegerGenerex ,但它们似乎不符合我的要求。 They actually fail to process some of the regex patterns (like a{60000} ) or for others (eg (A|B|C|D|E|F) ) they just don't produce all possible values.它们实际上无法处理某些正则表达式模式(如a{60000} )或其他模式(例如(A|B|C|D|E|F) ),它们只是不会产生所有可能的值。 Since I didn't find any another appropriate solution - I've created my own library.由于我没有找到任何其他合适的解决方案 - 我创建了自己的库。

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

This library can be used to generate both matching and non-matching string.该库可用于生成匹配和不匹配的字符串。

There is also artifact on maven central available. maven central 上也有可用的神器。

Usage example:使用示例:

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

I've gone the root of rolling my own library for that (In c# but should be easy to understand for a Java developer).我已经为此滚动了自己的库(在 c# 中,但对于 Java 开发人员来说应该很容易理解)。

Rxrdg started as a solution to a problem of creating test data for a real life project. Rxrdg 最初是为了解决为现实项目创建测试数据的问题。 The basic idea is to leverage the existing (regular expression) validation patterns to create random data that conforms to such patterns.基本思想是利用现有的(正则表达式)验证模式来创建符合这些模式的随机数据。 This way valid random data is created.这样就创建了有效的随机数据。

It is not that difficult to write a parser for simple regex patterns.为简单的正则表达式模式编写解析器并不难。 Using an abstract syntax tree to generate strings should be even easier.使用抽象语法树生成字符串应该更容易。

I am on flight and just saw the question: I have written easiest but inefficient and incomplete solution.我在飞行中,刚刚看到一个问题:我写了最简单但效率低下且不完整的解决方案。 I hope it may help you to start writing your own 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);
}

On stackoverflow podcast 11:在 stackoverflow 播客 11 上:

Spolsky: Yep.斯波尔斯基:是的。 There's a new product also, if you don't want to use the Team System there our friends at Redgate have a product called SQL Data Generator [ http://www.red-gate.com/products/sql_data_generator/index.htm] .还有一个新产品,如果你不想使用团队系统,我们在 Redgate 的朋友有一个名为 SQL 数据生成器的产品 [ http://www.red-gate.com/products/sql_data_generator/index.htm] . It's $295, and it just generates some realistic test data.它是 295 美元,它只是生成一些真实的测试数据。 And it does things like actually generate real cities in the city column that actually exist, and then when it generates those it'll get the state right, instead of getting the state wrong, or putting states into German cities and stuff like... you know, it generates pretty realistic looking data.它会在实际存在的城市列中生成真实的城市,然后当它生成这些城市时,它会让状态正确,而不是把状态弄错,或者将状态放入德国城市之类的......你知道,它会生成非常逼真的数据。 I'm not really sure what all the features are.我不太确定所有功能是什么。

This is probably not what you are looking for, but it might be a good starting off point, instead of creating your own.这可能不是您想要的,但它可能是一个很好的起点,而不是创建您自己的。

I can't seem to find anything in google, so I would suggest tackling the problem by parsing a given regular expression into the smallest units of work (\w, [xx], \d, etc) and writing some basic methods to support those regular expression phrases.我似乎在 google 中找不到任何东西,所以我建议通过将给定的正则表达式解析为最小的工作单元(\w、[xx]、\d 等)并编写一些基本方法来支持来解决这个问题那些正则表达式短语。

So for \w you would have a method getRandomLetter() which returns any random letter, and you would also have getRandomLetter(char startLetter, char endLetter) which gives you a random letter between the two values.因此,对于 \w 您将有一个方法 getRandomLetter() 返回任何随机字母,并且您还将拥有 getRandomLetter(char startLetter, char endLetter) 它为您提供两个值之间的随机字母。

You'll have to write your own parser, like the author of String::Random (Perl) did.您必须编写自己的解析器,就像 String::Random (Perl) 的作者所做的那样。 In fact, he doesn't use regexes anywhere in that module, it's just what perl-coders are used to.事实上,他没有在该模块的任何地方使用正则表达式,这正是 perl-coders 所习惯的。

On the other hand, maybe you can have a look at the source , to get some pointers.另一方面,也许您可​​以查看源代码以获取一些指示。


EDIT: Damn, blair beat me to the punch by 15 seconds.编辑:该死的,布莱尔以 15 秒的优势击败了我。

I know there's already an accepted answer, but I've been using RedGate's Data Generator (the one mentioned in Craig's answer) and it works REALLY well for everything I've thrown at it.我知道已经有一个公认的答案,但我一直在使用RedGate 的数据生成器(克雷格的答案中提到的那个),它对我抛出的所有内容都非常有效。 It's quick and that leaves me wanting to use the same regex to generate the real data for things like registration codes that this thing spits out.它很快,这让我想使用相同的正则表达式来为这个东西吐出的注册码之类的东西生成真实数据。

It takes a regex like:它需要一个正则表达式,如:

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

and it generates tons of unique codes like:它会生成大量独特的代码,例如:

LLK-32U

Is this some big secret algorithm that RedGate figured out and we're all out of luck or is it something that us mere mortals actually could do?这是 RedGate 想出来的一些大秘密算法,我们都不走运,还是我们这些凡人实际上可以做的事情?

It's far from supporting a full PCRE regexp, but I wrote the following Ruby method to take a regexp-like string and produce a variation on it.它远不支持完整的 PCRE 正则表达式,但我编写了以下 Ruby 方法来获取类似正则表达式的字符串并对其产生变体。 (For language-based 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

This question is very old, but I stumbled across it on my own search, so I will include a couple links for others who might be searching for the same functionality in other languages.这个问题很老了,但我在自己的搜索中偶然发现了它,所以我将为可能正在用其他语言搜索相同功能的其他人提供几个链接。

If you want to generate "critical" strings, you may want to consider:如果要生成“关键”字符串,您可能需要考虑:

EGRET http://elarson.pythonanywhere.com/ that generates "evil" strings covering your regular expressions EGRET http://elarson.pythonanywhere.com/生成覆盖正则表达式的“邪恶”字符串

MUTREX http://cs.unibg.it/mutrex/ that generates fault-detecting strings by regex mutation MUTREX http://cs.unibg.it/mutrex/通过正则表达式突变生成故障检测字符串

Both are academic tools (I am one of the authors of the latter) and work reasonably well.两者都是学术工具(我是后者的作者之一)并且工作得相当好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM