使用Regex生成字符串而不是匹配它们


108

我正在编写一个Java实用程序,可以帮助我生成大量数据以进行性能测试。这将是真正冷却到能够指定弦乐正则表达式让我产生吐出来匹配这个哪些东西。有没有已经烤好的东西可以用来做呢?还是有一个图书馆可以带给我大部分帮助?

谢谢


1
这是一个有用的Java库,提供使用正则表达式生成字符串的许多功能(随机生成,根据其索引生成字符串,生成所有字符串..)在此处查看
Mifmif 2014年

另一种选择可能是这个
弗拉季Varslavans

Answers:


40

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

又见https://github.com/mifmif/Generex所建议Mifmif

原始信息:

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

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

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


2
我不确定Xeger会这么好。它不能处理字符类。它无法识别一个简单的[\w]。看看他们Wiki的最后一行可以告诉我们。
约翰·雷德

2
还要注意,这些依赖于此,dk.brics.automaton因此准备添加第三方pom依赖项。大多数人都不介意,但是我希望有一些紧凑的东西。
Sridhar Sarnobat,

xeger和generex有替代方案。它没有所有这些缺点,并且不是过时的。请向下滚动到我的答案。
弗拉迪斯拉夫·瓦尔斯拉夫人

“首先,使用足够复杂的正则表达式,我相信这是不可能的。” - 严格来说并非如此:传递给某些东西的任何正则表达式也可以生成有效的输入。说明:正则表达式是Chomsky层次结构上的type-3,这意味着它们可以表示为FSM。单步执行FSM时,每个边都将解释为下一个字符的规则,因此FSM可用于解析生成序列。如果FSM具有到终端的路径,则可以确定有效顺序。因此,如果没有到终端的路径(这将是无用的正则表达式),那么这只是“不可能的”。
劳伦斯·瓦格菲尔德

22

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

范例:

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)问题。根据规则,这需要提出来。


11
看起来Generex是您自己的项目。您是否愿意根据本文中的规则帖子中提到这是您自己的项目?
Brian McCutchon '16


5

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

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

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


链接不再指向存储库。我会选择openhub.net/p/rxrdg。解决方案没有建立,但是?
Veverke

4

在stackoverflow播客11上:

Spolsky:是的。还有一个新产品,如果您不想使用团队系统,那么Redgate的我们的朋友就有一个名为SQL数据生成器的产品[ http://www.red-gate.com/products/sql_data_generator/index.htm]。它的价格为295美元,它会生成一些真实的测试数据。它所做的事情类似于在实际存在的city列中实际生成真实的城市,然后在生成城市时将使状态正确,而不是使状态错误,或者将状态放入德国城市和类似的东西中...您知道,它会生成非常逼真的外观数据。我不确定所有功能是什么。

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

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

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


4

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

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

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

用法示例:

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

3

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

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

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

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

LLK-32U

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


3

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

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

您可能要指出哪种类型的字符串用作模式输入。首先,从源代码确定这些事情并不是那么容易。其次,如果源代码中有任何错误或不明确之处,则无法查看它们是否是故意的。
Maarten Bodewes,2012年

StringTokenizer是一个遗留类,出于兼容性原因而保留,尽管在新代码中不鼓励使用它。建议任何寻求此功能的人改用String的split方法或java.util.regex包。
罗希特(Rohit)2014年

2

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

另一方面,也许您可​​以看一下source,以获得一些指针。


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


1

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

# 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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.