是否可以编写一个正则表达式,它将匹配给定字符集的任何子集
a1 ... an
?
即它应匹配任何字符串,其中任何一个字符最多出现一次,没有其他字符,字符的相对顺序无关紧要。
立即出现的一些方法:
1. [a1,...,an]*
或(a1|a2|...|an)*
- 这允许多个字符存在
2. (a1?a2?...an?)
- 没有多重存在,但相对顺序很重要 - 这匹配任何子序列但不匹配子集。
3. ($|a1|...|an|a1a2|a2a1|...|a1...an|...|an...a1)
,即写下所有可能的子序列(只需硬编码所有匹配的字符串:))当然,这是不可接受的。
我还猜测它在理论上可能是不可能的,因为在解析字符串时我们需要记住我们之前已经遇到过哪个字符,据我所知,正则表达式只能检查出正确的线性语言。
任何帮助将不胜感激。提前谢谢。
答案 0 :(得分:4)
这不符合language-agnostic
标签的要求,但是......
^(?:(?!\1)a1()|(?!\2)a2()|...|(?!\n)an())*$
<强> see a demo on ideone.com 强>
元素第一次匹配时,后面的捕获组会“检查”它。由于该组现在已参与该匹配,因此其相应的反向引用(例如,(?!\1)
)的负前瞻将永远不会再次匹配,即使该组仅捕获空字符串。这是一个未记录的功能,但仍支持多种功能,包括Java,.NET,Perl,Python和Ruby。
此解决方案还需要支持前向引用(即,在组本身之前出现在正则表达式中的给定捕获组(\1
)的引用)。这似乎比空团体噱头的支持要少得多。
答案 1 :(得分:3)
无法考虑如何使用单个正则表达式执行此操作,但这是使用n
正则表达式执行此操作的一种方法:(我将1
2
... {您的m
s {/ p> {1}} n
等
a
如果以上所有内容都匹配,则您的字符串是^[23..n]*1?[23..n]*$
^[13..n]*2?[13..n]*$
...
^[12..m]*n?[12..m]*$
的严格子集。
这是如何工作的:每一行都要求字符串完全:
12..mn
a particular one
a particular one
如果在每个元素依次被视为a particular one
时通过,我们知道:
根据需要。
为了完整性,我应该说,如果我接到“使用正则表达式”的命令,我只会这样做;如果没有,我会跟踪哪些允许的元素被看到,并迭代字符串的字符做明显的事情。
答案 2 :(得分:1)
不确定你是否可以获得扩展的正则表达式,但是通过简单遍历字符串很容易。
如果已经在字符串中看到任何允许的字符,则使用哈希(或数组或其他)来存储。然后,您只需遍历字符串的元素。如果你遇到一个不在你允许的集合中的元素,你就会纾困。如果它被允许,但你已经看过它,你也会拯救它。
在伪代码中:
foreach char a in {a1, ..., an}
hit[a1] = false
foreach char c in string
if c not in {a1, ..., an} => fail
if hit[c] => fail
hit[c] = true
答案 3 :(得分:0)
与Alan Moore类似,只使用\ 1,并且在看到之前没有引用捕获组:
#!/usr/bin/perl
my $re = qr/^(?:([abc])(?!.*\1))*$/;
foreach (qw(ba pabc abac a cc cba abcd abbbbc), '') {
print "'$_' ", ($_ =~ $re) ? "matches" : "does not match", " \$re \n";
}
我们匹配任意数量的块(外部(?:)),其中每个块必须包含“恰好是我们首选集合中的一个字符,后面跟不包含该字符的字符串”。
如果字符串可能包含换行符或其他有趣的东西,可能需要使用一些标志来制作^,$和。表现如预期,但这完全取决于特定的RE风味。
只是为了愚蠢,可以使用正向前瞻断言来有效地和两个正则表达式,所以我们可以通过断言上面的匹配来测试abc的任何排列,然后检查'是N个字符长并且由这些字符组成:
my $re2 = qr/^(?=$re)[abc]{3}$/;
foreach (qw(ba pabc abac a cc abcd abbbbc abc acb bac bca cab cba), '') {
print "'$_' ", ($_ =~ $re2) ? "matches" : "does not match", " \$re2 \n";
}