2016-05-08 43 views
0

我想多了解一些正则表达式。 Add a space on a string but counting right to left按正则表达式分组

这个表达式

preg_replace("/(?=(.{3})*(.{4})$)/", "-", "1231231234"); 

的结果是:123-123-1234

现在,我用的量词和组进行实验,但我不能让他们正常工作。

为什么这个(PHP)

preg_replace("/(?=(.{3})*(.{4})(.{4})$)/", "-", "1212312312345678"); 

这:

preg_replace("/(?=(.{3})*(.{4}){2}$)/", "-", "1212312312345678"); 

两个给我一个很大的8字符组作为输出

12-123-123-12345678 

我可能预期的结果第二种情况{2},但不是第一种情况。

预期的结果我打算是:。

12-123-123-1234-5678 

1)什么是逻辑上({4})({4})=({8}),而不是被2个不同的事件?

2)什么是适当的分组?

回答

1

你似乎误解如何该正则表达式的作品。让我打破它给你:

(?=   lookahead assertion: the following pattern must match, but 
      will not consume any of the text. 
    (.{3})* matches a series of 3 characters, any number of times. In 
      other words, this consumes characters in multiples of 3. 
    (.{4})$ makes sure there are exactly 4 characters left. 
) 

这种模式在要插入一个破折号-每一个地方产生空匹配。这就是为什么preg_replace("/(?=(.{3})*(.{4})$)/", "-", "1231231234");在正确的位置插入破折号 - 替换空字符串与插入相同。让我们来看看这一步一步,使用文本31231234为例:

  remaining text  remaining pattern  what happens 
step 0: 31231234   (.{3})*(.{4})$   (.{3})* matches one time 
step 1: 31234    (.{3})*(.{4})$   (.{3})* matches again 
step 2: 34     (.{3})*(.{4})$   (.{3})* fails to match another time 
step 3: 34     (.{4})$    (.{4}) fails to match -> backtrack 
step 5: 31234    (.{4})$    (.{4}) fails to match -> pattern failed to 
                match, no dash will be inserted. 

后的格局未能在位置0中的文本匹配,将再次在位置1检查(剩余的文本是1231234):

  remaining text  remaining pattern  what happens 
step 0: 1231234   (.{3})*(.{4})$   (.{3})* matches one time 
step 1: 1234    (.{3})*(.{4})$   (.{3})* matches again 
step 2: 4     (.{3})*(.{4})$   (.{3})* fails to match another time 
step 3: 4     (.{4})$    (.{4})$ matches -> dash will be inserted 
                here, giving "3-1231234" 

同样的事情再次发生3个字符后,让最终的结果3-123-1234。换言之,组(.{4})$指定在文本的最后4个字符中不应插入破折号。通过消耗最后4个字符,如果剩余的字符数少于4个字符,则无法匹配模式。这就是为什么(.{4})(.{4})$(.{4}){2}$都会产生8个字符的块 - 如果少于8个字符,则该模式不能匹配。

为了插入另一短跑在过去的8个字符,你必须使用4个字符.{4}两组,让其中一人可选:

(?=((.{3})*.{4})?(.{4})$) 
+0

感谢您的详细解释。还有一个问题。当使用$时,它实际上是从这一点开始的回溯搜索,还是某种从左至右的重复?例如,在这个简单的测试中:https://regex101.com/r/eE6zK7/1它表示该比赛是在20步后发现的。 – Rafael

+0

@Rafael:'$'只是一个字符串末尾的锚,它不会影响正则表达式的“方向”。如果你点击regex101上的“调试器”,你可以看到它是如何一步一步匹配的。 –

2

请注意,在这种情况下您使用的是lookaheads。与普通匹配不同,它们实际上并不消耗它们匹配的内容。

因此,在第一个例子中,有2个零宽度匹配,则第一123后的第一个,因此对于1231234第二123后先行相匹配,所述第二,所述先行匹配1234。您可能想使用其中一个在线正则表达式测试器来查看实际匹配的内容,我的选择将是regex101.com

因此,对于您的示例,您必须使前瞻符合最后4位数字(并且仅限它们),实现此目的的一种方法是(?=((.{3})*(.{4}))?(.{4})$),使第一部分可选。

请参阅regex101

+0

谢谢,是的,我正在玩regex101:○ ),是的,我需要了解的一件事是一种模式与其他模式相匹配。 TY。 – Rafael

1

(?=(.{3})*(.{4}){2}$)与2×4 = 8个字符每3×N个字符序列在端,其中N> = 0

为了匹配从端部每4xN字符,其中1 < = N < = 2,或每3×N个匹配并在最后8个字符,其中N> = 1,使用下面的字符序列:

preg_replace("/(?=(.{4}){1,2}$)|(?=(.{3})+.{8}$)/", "-", "1212312312345678"); 
+0

Iwteresting。目前我不认为我会使用垂直条,但这是一个很好的例子,谢谢。 – Rafael