2017年3月2日 星期四

RegExp 應用: lookahead , lookbehind

正規表示法一直都是我很推大家學習的東西,在字串處理上真的有很大很大的幫助以及好處,前幾天朋友工作上需要用到正規表示法處理字串,目的是:「在一個字串中找出連續數字 6~8 個」,這邊要注意的是,連續九個的話是不要的。

例如: 12345 XD Hi12345678ab666666cd987654321

要找出: 12345678 和 666666;但不可以找出 987654321 中的 98765432 或者 87654321

於是我第一個想到的東西就是 Lookahead 和 lookbehind。

先來看一下如果直接使用 \d{6,8} 會取出什麼:Visual Regex Tester
可以看到,直接使用 \d{6,8} 是會連後方的 987654321 取出來。

最後我給的的解法是:(?<!\d)\d{6,8}(?!\d),結果:Visual Regex Tester

重點就在前面的 (?<!\d) 和後面的 (?!\d) 這兩個表示法,他們分別代表的是
negative lookbehindnegative lookahead



先來看 lookahead , 意思是「往前看」,語法為:
  • Positive lookahead : X(?=Y)
  • Negative lookahead : X(?!Y)
解釋為: 我要找 X 而其後方必須/不可為 Y ;而其中 X 和 Y 都可以是一個合法的表達式。

這邊要注意的是,要找的是 X , Y 只是附註條件,並不會被收納在 match group 中

實際例子: \d(?=[AB]) ,我要找一個數字,且後方必須是 A 或 B

因此這個字串:123A4C5B67 中,符合的只有 3 和 5;要注意,不是 3A 和 5B是 3 和 5
結果:Visual Regex Tester

同樣的字串,若將 Pattern 改為:\d(?![AB]),則代表要找一個數字,且後方不能跟著A或B。如此一來符合的就是:123A4C5B67 中的: 1,2,4,6,7
結果:Visual Regex Tester

簡單來說可以想像類似於正規表示法中的 if 判斷,可以多多善用這個表示法。
-----

接著來看 lookbehind ,這邊要注意的是,並非所有程式語言都支援 lookbehind,我自己目前常用的程式語言中,似乎也只有 java 支援。(javascript 是不支援的喔)

lookbehind 就是向後看,同樣也分為 positive 和 negative:
  • Positive lookbehind : (?<=Y)X
  • Negative lookbehind: (?<!Y)X
※ 注意,條件是放在 X 之前

同樣舉實際例子:(?<=[OP]).,要找任何一個字,且前方必須是O或P。或也可以這樣來稱呼:「尋找接再 O 或 P 後面的一個文字」

字串:AOIOOXPAOCKNJS ,符合的就是 I,O,X,A,C
結果:Visual Regex Tester

Negtive 就不多舉例,能理解就好。
-----


那麼回到最開始的表示法:(?<!\d)\d{6,8}(?!\d),來解釋看看這一段到底在找什麼東西。

先將其拆為:(?<!\d) \d{6,8} (?!\d) 三段,很快就可以看出,要找的東西是「6~8 個連續數字」,但是「前面不能有數字(Negative lookbehind)」以及「後面不能跟著數字(Negative lookahead)」。

由於 {6,8} 是 Greedy ,會盡量多取,因此就可以限制找到的數字數量是在 6~8 ,並且排除連續 9 個(以上)數字的部分了。

10 則留言: