2011-09-08 104 views
4

我有以下的正则表达式:简化正则表达式?

(?i:^TPI$|^TIP$|^IPT$|^ITP$|^PIT$|^PTI$|^IP$|^PI$|^TI$|^IT$|^PT$|^TP$|^T$|^P$|^I$) 

我怎么能简化呢?我的正则表达知识是相当有限的。

我的要求是:

  • 可接受输入为 “T”, “P” 和 “I”
  • 值可能会在任何顺序
  • 接受每个值只有一个。 “TTI” 是无效的,但 “TI” 是有效的
  • 不区分大小写

我在过去使用

^(?i:[TPI]){1,3}$ 

,而且大多是工作。唯一的问题是它接受多个值“TTT”对于那个正则表达式是可以接受的,我需要这个失败)。

+0

这是什么语言? – Jaanus

+1

Jaanus:这应该是无关的几乎所有回答这个问题,除非您使用的是非常简单的实现还是一个从上世纪70年代左右。 – Joey

+0

仅供参考,我已经添加了'ITP',请删除它是否应该在那里。 –

回答

6

我们可以尝试一种不同的方式。你所做的尝试允许一些字符串滑过你不想要的。也就是说,一切都有重复。在下面,我将用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 

根据您的语言 - 只要它使那些东西方便性和可读性。

+1

我认为你已经有了你的60k – sidyll

+0

还没有,而且这个问题的流量非常低---) – Joey

+0

不应该,这个问题真的很好,你想出的答案只是真棒。如果它是固定长度(3),我已经回答'/(?: T()| P()| I()){3} \ 1 \ 2 \ 3 /'但你是更简单的用于这样的任务。 – sidyll

1

这里是为子孙后代着想另一个答案。

^(?i:([TPI])(?!.*?\1)){1,3}$