2010-01-24 78 views
6

我對正則表達式相當不錯,現在我再次嘗試理解前瞻和後向斷言。他們大多數都有道理,但我不太清楚訂單如何影響結果。我一直在尋找this site,它在表達式之前放置向後看,並且在表達式之後向前看。我的問題是,這是否會改變什麼?最近在SO上的一個回答把這個前瞻放在導致我混亂的表達之前。正則表達式順序

回答

9

當教程介紹lookarounds,他們往往會選擇每一個最簡單的使用情況。因此,他們會使用(?<!a)b('b''之前未加'a')或q(?=u)('q'後加'u')的示例。這只是爲了避免讓注意力分散的細節混淆解釋,但它傾向於創造(或加強)這樣的印象,即往後看和向後看起來應該以某種順序出現。我花了相當長的時間來克服這個想法,而且我也看到其他幾個人也受到了這個問題的困擾。

試着看一些更實際的例子。一個涉及很多問題涉及驗證密碼;例如,確保新密碼長度至少爲六個字符並且至少包含一個字母和一個數字。要做到這一點的方法之一是:

^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$ 

的字符類[A-Za-z0-9]{6,}可以匹配所有字母或所有數字,讓你用的向前看符號,以確保有各自的至少一個。在這種情況下,您必須首先執行,因爲正則表達式的後面部分必須能夠檢查整個字符串。

又例如,假設您需要查找單詞「there」的所有出現位置,除非其前面帶有引號。顯而易見的正則表達式是(?<!")[Tt]here\b,但是如果您搜索的是大型語料庫,那可能會造成性能問題。正如所寫的那樣,該正則表達式將在文本中的每一個位置都做負面的後臺關係,並且只有當它成功時纔會檢查正則表達式的其餘部分。

每一個正則表達式引擎都有自己的長處和短處,但其中的一點是,它們更快地找到文字字符的固定序列,而不是其他任何東西 - 序列越長越好。這意味着它可以大大加快做回顧後最後,即使這意味着這個詞兩次匹配:

[Tt]here\b(?<!"[Tt]here) 

所以管理lookarounds的位置,規則是沒有規則;你把它們放在最有意義的地方。

1

1(?=ABC)表示 - 查找1,並且匹配(但不捕獲)後面的ABC
(?<=ABC)1表示在當前位置之前匹配(但不捕獲)ABC,並繼續匹配1
因此,通常情況下,您會在前面的表達式和後面的後面放置向前看。

當我們在表達式後面放置一個倒序時,我們正在重新檢查我們已經匹配的字符串。當你有複雜的條件時,這很常見(你可以把它想象成AND的正則表達式)。例如,拿在最近這次的回答看看由Daniel Brückner

.&.(?<! &) 

首先,你捕獲兩個字符之間的符號。接下來,你檢查他們是不是空格(\S&\S不會在這裏工作,OP想要捕獲1&_)。

+0

'[^]&[^]'可能比'。&。(?<!&)'更容易理解。 – Gumbo 2010-01-24 09:30:12

+2

這將不會匹配「this&that」,而lookbehind版本會。一個有效的等價物應該是:'\ S&|&\ S' – 2010-01-24 09:47:09

4

我覺得在示例中比解釋更容易。讓我們這個表達式:

(?<=\d)(?=(.)\1)(?!p)\w(?<!q) 

這意味着:

  1. (?<=\d) - 確保賽前位置是一個數字會發生什麼。
  2. (?=(.)\1) - 確保我們在這個(相同)位置匹配的任何字符後面跟着一個自身副本(通過反向引用)。
  3. (?!p) - 確保接下來不是p
  4. \w - 匹配字母,數字或下劃線。請注意,這是我們第一次真正匹配並使用角色。
  5. (?<!q) - 確保我們迄今爲止匹配的內容不會以q結尾。

所有這一切都將匹配像abc5ddx9xx但不5d6qqasd6ppadd字符串。請注意,每個斷言獨立工作。它只是停下來,環顧四周,如果一切順利的話,讓匹配繼續下去。

還要注意的是,在大多數(可能全部)實現中,lookbehinds具有固定長度的限制。您不能在其中使用重複/可選性運算符,如?,*+。這是因爲要匹配一個模式,我們需要一個起點 - 否則我們必須嘗試匹配字符串中每個點的每個後視圖。

此正則表達式的串a3b5ddx的樣品運行如下:

  1. 文本光標位置:0
    1. 嘗試第一回顧後在位置匹配-1(因爲\d總是匹配1個字符)。我們不能匹配負指數,所以失敗並提前光標。
  2. 文本光標位置:1.
    1. 嘗試匹配位置0 a第一回顧後不匹配\d如此失敗,再次向前移動光標。
  3. 文本光標位置:2
    1. 嘗試在第一回顧後在位置1 3匹配不匹配\d所以保持光標不變,並且繼續匹配。
    2. 嘗試匹配位置2處的第一個預測。b匹配(.)並被捕獲。 5不匹配\1(這是被捕獲的b)。因此,失敗並推進遊標。
  4. 文本光標位置:3
    1. 嘗試匹配在2位b第一回顧後不匹配\d如此失敗,再次向前移動光標。
  5. 文本光標位置:4
    1. 嘗試在第一回顧後在位置3 5匹配不匹配\d所以保持光標不變,並且繼續匹配。
    2. 嘗試匹配位置4處的第一個預測。d匹配(.)並被捕獲。第二個d確實匹配\1(這是第一個捕獲的d)。允許匹配從我們離開的地方繼續。
    3. 嘗試匹配第二個預測。在位置4處的bp不匹配,並且由於這是負向前視,所以我們想要;允許匹配繼續。
    4. 嘗試匹配\w的第4位。b比賽。由於我們已經消耗了一個字符並繼續,因此前進光標同樣將此標記爲比賽的開始。
  6. 文本光標位置:5.
    1. 嘗試匹配在第4位的第二回顧後(因爲q總是匹配1個字符)。 d不符合q這是我們想要從負面看後面。
    2. 意識到我們處於正則表達式的末尾,並通過將匹配字符串從當前位置(4到5)返回到d來報告成功。