关于java:使用Regex生成字符串而不是匹配它们

关于java:使用Regex生成字符串而不是匹配它们

Using Regex to generate Strings rather than match them

我正在编写一个Java实用程序,可以帮助我生成用于性能测试的数据负载。 能够为Strings指定一个正则表达式真的很酷,这样我的生成器就会吐出与此匹配的东西。 有没有已经烤好的东西可以用来做呢? 还是有一个图书馆可以带给我大部分帮助?

谢谢


编辑:

如评论中所述,Google Code提供了一个可实现此目的的库:
http://code.google.com/p/xeger

另请参阅Mifmif建议的https://github.com/mifmif/Generex

原始信息:

首先,我相信使用足够复杂的正则表达式,这是不可能的。但是,您应该可以将一些东西放在一起进行简单的正则表达式。

如果您查看类java.util.regex.Pattern的源代码,您会发现它使用Node实例的内部表示形式。每个不同的模式组件都有自己的Node子类实现。这些节点被组织成一棵树。

通过产生遍历此树的访问者,您应该能够调用重载的生成器方法或某种将某些东西拼凑在一起的Builder。


帮助原始海报为时已晚,但可以帮助新来者。 Generex是一个有用的Java库,提供了许多使用正则表达式生成字符串的功能(随机生成,基于其索引生成字符串,生成所有字符串...)。

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

揭露

这篇文章中提到的项目属于用户回答(Mifmif)问题。根据规则,这需要提出来。


Xeger(Java)也可以做到这一点:

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

我已经为此滚动了自己的库(在c#中,但对于Java开发人员来说应该很容易理解)。

Rxrdg最初是作为为现实生活项目创建测试数据的问题的解决方案。基本思想是利用现有(正则表达式)验证模式来创建符合此类模式的随机数据。这样,可以创建有效的随机数据。

为简单的正则表达式模式编写解析器并不难。使用抽象语法树生成字符串应该更加容易。


在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]. It's $295, and it just generates some realistic test data. 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.

这可能不是您想要的,但它可能是一个不错的起点,而不是创建自己的起点。

我似乎在google中找不到任何内容,因此建议通过将给定的正则表达式解析为最小的工作单位(\ w,[xx],\ d等)并编写一些基本方法来解决该问题那些正则表达式短语。

因此,对于\ w,您将具有getRandomLetter()方法,该方法返回任何随机字母,并且您还将具有getRandomLetter(char startLetter,char endLetter),该方法为您提供两个值之间的随机字母。


我在飞行中,只看到一个问题:我写了最简单但效率低下和不完整的解决方案。我希望它可以帮助您开始编写自己的解析器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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);
}

我知道已经有一个可以接受的答案,但是我一直在使用RedGate的数据生成器(在Craig的答案中提到的那个),并且对于我投入的所有内容,它都非常有效。它的速度很快,而我却想使用相同的正则表达式来生成诸如此类的注册码之类的真实数据。

它需要一个正则表达式,例如:

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

并生成大量独特的代码,例如:

1
LLK-32U

这是RedGate想出的一个大秘密算法,我们都走运了吗,还是我们凡人实际上可以做的事情?


这个问题确实很老,尽管这个问题对我来说是实际的。
我尝试过xeger和Generex,它们似乎不符合我的要求。
它们实际上无法处理某些正则表达式模式(例如a{60000}),而对于其他正则表达式模式(例如(A|B|C|D|E|F)),它们只是无法产生所有可能的值。由于找不到其他合适的解决方案-我创建了自己的库。

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

在Maven Central上也有可用的工件。

用法示例:

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

您必须编写自己的解析器,就像String :: Random(Perl)的作者一样。实际上,他在该模块中的任何地方都没有使用正则表达式,而这正是perl编码器所习惯的。

另一方面,也许您可??以看一下源代码,以获得一些指针。

编辑:该死,布莱尔以15秒的优势击败了我。


这个问题很老,但是我在自己的搜索中偶然发现了这个问题,因此我将提供几个链接,供其他人使用其他语言搜索相同功能。

  • 这里有一个Node.js库:https://github.com/fent/randexp.js
  • 这里有一个PHP库:https://github.com/icomefromthenet/ReverseRegex
  • PHP伪造者程序包包括实现此目的的" regexify"方法:https://packagist.org/packages/fzaninotto/faker

它远不支持完整的PCRE正则表达式,但是我编写了以下Ruby方法来获取类似regexp的字符串并对其进行变体。 (对于基于语言的验证码。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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

如果要生成"关键"字符串,则可能需要考虑:

EGRET http://elarson.pythonanywhere.com/
生成覆盖您正则表达式的"邪恶"字符串

MUTREX http://cs.unibg.it/mutrex/
通过正则表达式突变生成故障检测字符串

两者都是学术工具(我是后者的作者之一),并且运作良好。


推荐阅读