2016-05-08 42 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