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

Posted

技术标签:

【中文标题】使用正则表达式生成字符串而不是匹配它们【英文标题】:Using Regex to generate Strings rather than match them 【发布时间】:2010-09-06 12:46:34 【问题描述】:

我正在编写一个 Java 实用程序,它可以帮助我生成大量数据以进行性能测试。能够为字符串指定正则表达式以便我的生成器吐出与此匹配的内容,这将是 真的 很酷。有什么东西已经烤好了我可以用来做这个吗?还是有一个图书馆可以让我大部分时间到达那里?

谢谢

【问题讨论】:

这是一个有用的java library,它提供了许多使用正则表达式生成字符串的功能(随机生成,根据它的索引生成字符串,生成所有字符串..)检查一下here跨度> 另一种选择可能是this 【参考方案1】:

编辑:

关于这个问题的建议库的完整列表:

    Xeger* - Java Generex* - Java Rgxgen - Java rxrdg - C#

* - 取决于dk.brics.automaton

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

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

原文:

首先,使用足够复杂的正则表达式,我相信这是不可能的。但是你应该能够为简单的正则表达式组合一些东西。

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

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

【讨论】:

我不确定 Xeger 是否那么好。它不能处理字符类。它无法识别简单的[\w]。看看their wiki 的最后一行就知道了。 请注意,这些依赖于dk.brics.automaton,因此请准备好添加第 3 方 pom 依赖项。大多数人不介意,但我希望有更紧凑的东西。 有 xeger 和generex 的替代品。它没有所有这些缺点,也没有过时。请向下滚动到我的答案。 “首先,使用足够复杂的正则表达式,我相信这是不可能的。” -- 这不完全正确:任何通过something 的正则表达式也可以生成有效输入。说明:正则表达式在乔姆斯基层次结构中是类型 3,这意味着它们可以表示为 FSM。在单步执行 FSM 时,每条边都被解释为下一个字符的规则,因此 FSM 可用于解析生成序列。如果 FSM 具有到终端的路径,则可以确定有效序列。所以,只有没有通往终端的路径(这将是一个无用的正则表达式),这才是“不可能的”。【参考方案2】:

现在帮助原始海报为时已晚,但它可以帮助新人。 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)问题的用户。根据rules,这需要提出。

【讨论】:

看起来 Generex 是你自己的项目。根据here 的规则,您介意在您的帖子中提及这是您自己的项目吗?【参考方案3】:

Xeger (Java) 也能做到:

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

【讨论】:

Xeger 运行良好。但请确保您在类路径或 pom/gradle 中有 automaton jar【参考方案4】:

这个问题真的很老了,虽然这个问题对我来说是真实的。 我试过xeger 和Generex,但它们似乎不符合我的要求。 他们实际上无法处理某些正则表达式模式(如a60000)或其他模式(例如(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

【讨论】:

我试过 RxGen,效果比 Xeger 和 Generex 好很多 但是您的库不支持前瞻和后瞻,即使使用蛮力重新生成,它有时仍会产生无效的字符串。 该库有一些限制,在自述文件部分中有描述。【参考方案5】:

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

Rxrdg 最初是为了解决为现实项目创建测试数据的问题。基本思想是利用现有的(正则表达式)验证模式来创建符合这些模式的随机数据。这样就创建了有效的随机数据。

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

【讨论】:

链接不再指向存储库。我会选择openhub.net/p/rxrdg。但是,该解决方案无法构建?【参考方案6】:

关于 *** 播客 11:

斯波尔斯基:是的。还有一个新产品,如果你不想使用 Team System,我们在 Redgate 的朋友有一个产品叫做 SQL 数据生成器 [http://www.red-gate.com/products/sql_data_generator/index.htm]。它是 295 美元,它只是生成一些真实的测试数据。它会在实际存在的城市列中生成真实的城市,然后当它生成这些城市时,它会让状态正确,而不是把状态弄错,或者将状态放入德国城市之类的......你知道,它会生成非常逼真的数据。我不太确定所有功能是什么。

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

我似乎在 google 中找不到任何东西,所以我建议通过将给定的正则表达式解析为最小的工作单元(\w、[xx]、\d 等)并编写一些基本的来解决这个问题支持这些正则表达式短语的方法。

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

您可能想要指出使用哪种字符串作为模式输入。首先,从源代码中确定这些东西并不是那么容易。其次,如果源代码有任何错误或不清楚的地方,没有办法看出是不是故意的。 StringTokenizer 是一个遗留类,出于兼容性原因保留,但不鼓励在新代码中使用它。建议任何寻求此功能的人使用 String 的 split 方法或 java.util.regex 包。【参考方案8】:

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

另一方面,也许你可以看看the source,以获得一些指导。


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

【讨论】:

【参考方案9】:

我知道已经有一个公认的答案,但我一直在使用 RedGate 的数据生成器(克雷格的答案中提到的那个),它对我提出的所有问题都非常有效。它很快,这让我想使用相同的正则表达式来为这个东西吐出的注册码之类的东西生成真实数据。

它需要一个像这样的正则表达式:

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

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

LLK-32U

这是 RedGate 发现的一些大秘密算法,我们都不走运,还是我们这些凡人实际上可以做到的事情?

【讨论】:

【参考方案10】:

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

# 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!( /\s2,/, ' ' )
    out
  end
end

class Array
  def random
    self[ rand( self.length ) ]
  end
end

【讨论】:

【参考方案11】:

这个问题很老了,但我在自己的搜索中偶然发现了它,所以我会为可能正在用其他语言搜索相同功能的其他人提供几个链接。

这里有一个 Node.js 库:https://github.com/fent/randexp.js 这里有一个 php 库:https://github.com/icomefromthenet/ReverseRegex PHP faker 包包含一个“regexify”方法来完成这个:https://packagist.org/packages/fzaninotto/faker

【讨论】:

【参考方案12】:

如果您想生成“关键”字符串,您可能需要考虑:

白鹭http://elarson.pythonanywhere.com/ 生成覆盖正则表达式的“邪恶”字符串

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

两者都是学术工具(我是后者的作者之一)并且工作得相当好。

【讨论】:

以上是关于使用正则表达式生成字符串而不是匹配它们的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式只使用awk打印匹配的字符串而不是整行

python - re正则匹配模块

pcre和正则表达式的误点

正则表达式方法

正则表达式与 C# 中的 OR 条件最长匹配

.Net 正则表达式匹配 $ 与字符串的结尾而不是行的结尾,即使启用了多行