我们可以尝试一种不同的方式。你所做的尝试允许一些字符串滑过你不想要的。也就是说,一切都有重复。在下面,我将用PowerShell做一些实验来展示解决方案。首先,我们需要所有可能的字符串,我们可以期待作为输入:
$tests = 'TPI'[0..2]|%{$a=$_;"$a"; 'TPI'[0..2]|%{$b=$_;"$a$b"; 'TPI'[0..2]|%{"$a$b$_"}}} | sort
这将产生以下值的序列(I格式化他们在同一行,但他们出来每行一个通常情况下):
$tests
I II III IIP IIT IP IPI IPP IPT IT ITI ITP ITT P PI PII PIP PIT PP PPI PPP PPT PT PTI PTP PTT T TI TII TIP TIT TP TPI TPP TPT TT TTI TTP TTT
这当然亦是正则表达式
^(?i:[TPI]){1,3}$
将匹配。
我们可以限制我们想要通过使用所谓的负先行断言,以匹配其将匹配只有一些文字是以下,但不会实际匹配的文本本身,从而允许其被捕获你有上面的模式。这可以通过(?!)
完成,您可以在!
之后插入一些子表达式。让我们尝试并限制输入不带有两个I
,二P
或两个T
开始:
$tests -match '^(?!II|PP|TT)(?i:[TPI]{1,3})$'
I IP IPI IPP IPT IT ITI ITP ITT P PI PII PIP PIT PT PTI PTP PTT T TI TII TIP TIT TP TPI TPP TPT
正如你可以看到,那些从结果了。如果我们使用捕获组和反向引用,我们可以简化它。通常小括号(除非以(?
开头)捕获它们内部匹配的内容,并且可以在匹配后使用它来从匹配或替换中提取部分。但你也可以在模式中使用它本身在很多正则表达式引擎中(实际上,我认为没有引擎允许负向预测,但而不是模式中的反向引用)。所以II|PP|TT
可以写成(.)\1
,它只是说“一封信,后面是完全相同的字母”,因为\1
是反向引用,指向与(.)
匹配的任何内容。
现在我们仍然有我们不希望几个值,即一切有两个相同的字母在位置2和3的位置1和3,我们可以摆脱前者的有以下:
$tests -match '^(?!.?(.)\1)(?i:[TPI]{1,3})$'
I IP IPI IPT IT ITI ITP P PI PIP PIT PT PTI PTP T TI TIP TIT TP TPI TPT
中的.?
现在开始说“相匹配的字符或没有”的延伸,因此我们有什么前两排除比赛用到底重复。对于第二组,我们只需要排除的样子(.).\1
匹配,即一个字母,紧接着又和随后的第一个重复。我们可以只把另一.?
,捕获组和反向引用之间,即一个可选的信延长上述正则表达式:
$tests -match '^(?!.?(.).?\1)(?i:[TPI]{1,3})$'
I IP IPT IT ITP P PI PIT PT PTI T TI TIP TP TPI
现在正是你想要表示该集合。最终的正则表达式是
^(?!.?(.).?\1)(?i:[TPI]{1,3})$
它比以前更短,这是肯定的。是否更简单可能需要辩论,因为它可能需要一些解释。对于另一个答案中更加压缩的方法,情况更是如此。它确实短一些,但这就是我的答案,我们争夺选票,我只能说我不喜欢它;-) ...只是在开玩笑。但对于这样的事情,我想将基本模式与排除规则分开确实对于可读性有意义。
另一种选择可能是验证基本模式与正则表达式,即您的初始方法。然后使用代码来拒绝重复这可能看起来像
($s.ToLowerInvariant().ToCharArray() | select -Unique).Count -eq $s.Length
根据您的语言 - 只要它使那些东西方便性和可读性。
这是什么语言? – Jaanus
Jaanus:这应该是无关的几乎所有回答这个问题,除非您使用的是非常简单的实现还是一个从上世纪70年代左右。 – Joey
仅供参考,我已经添加了'ITP',请删除它是否应该在那里。 –