使用正则表达式生成字符串而不是匹配它们 [已关闭]

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

提问人:Andrew Harmel-Law 提问时间:8/22/2008 最后编辑:SaikatAndrew Harmel-Law 更新时间:5/27/2023 访问量:111596

问:


我们不允许提出有关书籍、工具、软件库等建议的问题。您可以编辑问题,以便用事实和引文来回答。

6个月前关闭。

社群在 6 个月前审查了是否重新打开这个问题,并将其关闭:

原始关闭原因未解决

我正在编写一个 Java 实用程序,它可以帮助我生成大量用于性能测试的数据。如果能够为 Strings 指定一个正则表达式,以便我的生成器吐出与此匹配的东西,那将非常酷。

有没有已经烤好的东西可以用来做这件事?或者有没有一个图书馆可以让我大部分时间到达那里?

Java 正则表达式

评论


答:

2赞 Espo 8/22/2008 #1

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

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


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

评论

0赞 Vladimir Dyuzhev 5/18/2023
你只在年轻时编写自己的解析器。
57赞 Cheekysoft 8/22/2008 #2

首先,对于足够复杂的正则表达式,我认为这是不可能的。但是你应该能够把一些东西放在一起,用于简单的正则表达式。

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

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

评论

2赞 John Red 2/13/2017
我不确定 Xeger 有那么好。它不能处理字符类。它无法识别简单的 .看看他们 wiki 的最后一行就知道了。[\w]
2赞 Sridhar Sarnobat 7/24/2018
另请注意,这些依赖于,因此请准备好添加第三方 pom 依赖项。大多数人并不介意这一点,但我希望有一些更紧凑的东西。dk.brics.automaton
0赞 Vladislav Varslavans 4/24/2020
xeger 和 generex 有替代品。它缺乏所有这些缺点,也没有过时。请向下滚动到我的答案。
3赞 Lawrence Wagerfield 6/7/2020
“首先,对于足够复杂的正则表达式,我相信这是不可能的”——这并不完全正确:任何传递到某物的正则表达式也可以生成有效的输入。解释:正则表达式是 Chomsky 层次结构中的第 3 类,这意味着它们可以表示为 FSM。单步执行 FSM 时,每条边都被解释为下一个字符的规则,因此 FSM 可用于解析生成序列。如果 FSM 具有通往终端的路径,则可以确定有效的序列。因此,只有当没有通往终端的路径(这将是一个无用的正则表达式)时,它才是“不可能”的。
4赞 Craig 8/22/2008 #3

在 stackoverflow 播客 11 上:

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

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

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

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

2赞 J Wynia 10/29/2008 #4

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

它需要一个正则表达式,如下所示:

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

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

LLK-32U

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

1赞 Gavin Kistner 11/11/2008 #5

它远不支持完整的 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!( /\s{2,}/, ' ' )
    out
  end
end

class Array
  def random
    self[ rand( self.length ) ]
  end
end
5赞 Goran 1/5/2009 #6

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

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

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

评论

0赞 Veverke 7/22/2015
链接不再指向存储库。我会和 openhub.net/p/rxrdg 一起去。解决方案没有建立,但是?
21赞 Wilfred Springer 10/20/2009 #7

Xeger (Java) 也能够做到这一点:

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

评论

2赞 Delicia Brummitt 9/8/2016
Xeger 工作得很好。但是请确保您在类路径或pom/gradle中有自动机jar
4赞 R dhabalia 8/28/2012 #8

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

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

评论

1赞 Maarten Bodewes 11/25/2012
您可能希望指示用作模式输入的字符串类型。首先,从源代码中确定这些事情并不是那么容易。其次,如果源代码中有任何错误或不明确之处,就无法看出它们是有意还是无意。
0赞 Rohit 3/2/2014
StringTokenizer 是一个旧类,出于兼容性原因而保留该类,尽管不建议在新代码中使用它。建议任何寻求此功能的人改用 String 的 split 方法或 java.util.regex 包。
25赞 Mifmif 7/10/2014 #9

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

评论

12赞 Brian McCutchon 6/3/2016
看起来Generex是你自己的项目。根据这里的规则,你介意在你的帖子中提到这是你自己的项目吗?
0赞 Angelo Gargantini 3/16/2017 #10

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

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

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

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

1赞 Everett 7/17/2017 #11

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

19赞 Vladislav Varslavans 11/12/2019 #12

这个问题真的很老,尽管这个问题对我来说是真实的。 我尝试过 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

评论

2赞 spacether 11/9/2020
我尝试了 RxGen,它的效果比 Xeger 和 Generex 好得多
0赞 shinzou 5/19/2021
但是你的库不支持前瞻和后视,即使使用暴力再生,它有时仍然会产生无效的字符串。
0赞 Vladislav Varslavans 5/19/2021
该库有一些限制,这些限制在自述文件部分中进行了描述。
0赞 Vladimir Dyuzhev 5/18/2023
像魅力一样工作。我正在使用它来生成 XSD 架构的示例 XML。