符合正则表达式的随机字符串

29

你如何创建一个符合特定正则表达式的随机字母数字字符串?

这个问题是为了创建满足密码要求的初始密码。


8个回答

19

嗯,只是思考,但对于足够松散的随机定义和足够严格的正则表达式定义,我认为生成匹配正则表达式的随机输入是可行的。我正在考虑经典的形式定义,它仅允许使用()|*和字母字符。

正则表达式可以映射到称为确定性有限状态自动机的形式机器中。这样的机器是一个带有特定节点的有向图,称为终态,一个称为初始状态的节点,并在每个边上用字母表示。如果可以从初始状态开始并通过图中标记为每个字符的一条边遍历到终止状态,则该单词将被正则表达式接受。

可以构建图,然后从终态开始向后遍历随机边缘,跟踪路径。在标准构造中,图中的每个节点都可以从初始状态到达,因此您无需担心犯下不可恢复的错误并需要回溯。如果到达初始状态,请停止并向前读取路径。那就是正则表达式的匹配结果。

但不能保证何时或是否会到达初始状态。必须弄清楚生成的字符串在什么意义上是“随机的”,以及在什么意义上您希望首先从语言中获得随机元素。

也许这是思考问题的起点!

现在我已经写出来了,我认为重复解析选择以简化正则表达式模式,直到剩下一个简单的字符串可能会更简单。找到模式中第一个非字母字符。如果它是*,则复制前面的项多次并删除*。如果是|,则选择保留OR的哪个项并删除其余项。对于左括号,做相同的事情,但查看匹配右括号后面的字符。如果首先将正则表达式解析为树表示形式,则可能更容易处理括号分组结构。

对于那些担心判断正则表达式是否匹配等同于停机问题的人来说,不,正则语言是相当规范的。你可以确定任何两个正则表达式是否描述了相同的接受字符串集。你基本上可以制作上面的机器,然后按照算法生成规范的最小等效机器。对于两个正则表达式做同样的事情,然后检查生成的最小机器是否等效,这很简单。


2
到目前为止,我的简化答案(使用https://dev59.com/3XVD5IYBdhLWcg3wNIrc上的已接受答案,直到它与您的正则表达式匹配)看起来很有前途 :) 尽管如此,我还是投了您一票,因为您非常详尽。 - Alvaro Rodriguez
老实说,我会制作一个随机密码生成器,并让它不断生成,直到我得到符合所有条件的密码。^-^ - RCIX
你可能想看一下Fare项目(C#)。在这里查看答案:http://stackoverflow.com/questions/5841454/convert-nfa-to-dfa/8240209#8240209 - Nikos Baxevanis
为什么不向前看呢?你知道在开头应该有其中之一,然后,你的语法规定必须有其中之一。这样你甚至可以生成CFG,而不仅仅是正则表达式! - Little Alien

19

Perl中的String::Random可以从正则表达式的子集中生成随机字符串:

#!/usr/bin/perl

use strict;
use warnings;

use String::Random qw/random_regex/;

print random_regex('[A-Za-z]{3}[0-9][A-Z]{2}[!@#$%^&*]'), "\n";

2
最好使用已经准备好的东西,它恰好满足您的需求。 - Daniel Hershcovich

6
如果您有特定的问题,那么您可能已经想好了一个具体的正则表达式。我会将该正则表达式转换成简单易懂的人类语言,并从那里开始工作。
我猜测创建一个通用的正则表达式随机匹配生成器是可能的,但这可能比处理一个特定的情况要更加困难,即使该情况每年都会发生几次变化。
(实际上,在最一般的意义上生成随机匹配可能是不可能的 - 我模糊地记得“任何字符串是否与此正则表达式匹配”的问题是伪装成停机问题的问题。使用一个非常简化的正则表达式语言,您可能会更幸运。)

正则表达式是可配置的,所以我不知道它会是哪一个。虽然我可以猜测大致内容,但无法确定具体细节。 - Alvaro Rodriguez
这是一个真诚的问题:是否有一种 RegExp 没有任何字符串与其匹配???我想不到。谢谢! - Manuel Araoz

4

我写了Parsley,它由Lexer和Generator组成。

  • Lexer用于将类似于正则表达式的字符串转换为一系列标记。
  • Generator使用这些标记生成指定数量的代码。
$generator = new \Gajus\Parsley\Generator();

/**
 * Generate a set of random codes based on Parsley pattern.
 * Codes are guaranteed to be unique within the set.
 *
 * @param string $pattern Parsley pattern.
 * @param int $amount Number of codes to generate.
 * @param int $safeguard Number of additional codes generated in case there are duplicates that need to be replaced.
 * @return array
 */
$codes = $generator->generateFromPattern('FOO[A-Z]{10}[0-9]{2}', 100);

上面的示例将生成一个包含100个代码的数组,每个代码都以"FOO"为前缀,后跟来自"ABCDEFGHKMNOPRSTUVWXYZ23456789"干草堆中的10个字符和来自"0123456789"干草堆中的2个数字。

好的,你改了名字是因为“Parsley name was taken by another library”,但没问题。 - Richard Barker

3

这个 PHP 库看起来很有前途:ReverseRegex

像其他所有库一样,它只处理正则表达式的一个子集,但它能够处理相当复杂的事情,如英国邮政编码:

([A-PR-UWYZ]([0-9]([0-9]|[A-HJKSTUW])?|[A-HK-Y][0-9]([0-9]|[ABEHMNPRVWXY])?) ?[0-9][ABD-HJLNP-UW-Z]{2}|GIR0AA)

输出

D43WF
B6 6SB
MP445FR
P9 7EX
N9 2DH
GQ28 4UL
NH1 2SL
KY2 9LS
TE4Y 0AP

1
你需要编写一个字符串生成器,它可以解析正则表达式,并为随机长度的字符范围生成随机成员,等等。
相对容易的方法是编写一个具有某些规则的随机密码生成器(以小写字母开头,至少有一个标点符号、大写字母和数字,至少 6 个字符等),然后编写你的正则表达式,使符合这些规则的任何密码都是有效的。

0

假设您需要满足最小长度和3-of-4*(或类似)要求,我会倾向于使用一个好的密码生成器。

我以前构建过几个(基于Web和命令行),从未不得不跳过超过一个生成的字符串才能通过3-of-4规则。

  • 3-of-4:必须至少具备以下三个特征之一:小写字母、大写字母、数字、符号

0

这是可能的(例如,Haskell正则表达式模块有一个测试套件,可以自动生成应该匹配某些正则表达式的字符串)。

然而,对于手头的简单任务,您最好使用简单的密码生成器,并使用您的正则表达式过滤其输出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接