2014年8月8日 星期五

Regular Expression入門

前言

  • Regular expression一般翻譯做「正規表達式」之類的,但其實翻譯不重要,因為看了翻譯還是不知道他的用途。
  • 在字串裡搜尋字串是大家很常用的功能,例如在硬碟內找某個檔案,或者是在文章內找某個字。
  • 但更常用的是搜尋「某個pattern」,例如想要找「連續7個數字」或是「三個字母接一個特殊字元,後面又有1或2個數字」,就需要用到regular expression!
  • 很多程式語言(或者script)都會有regular expression,使用上大同小異,花點時間學習這些火星文,一生受用無窮。
  • 以下以autohotkey環境來講解,其實大部分也是翻譯自autohotkey的官方文件
  • 測試regular expression的工具

RegExMatch

  • 在開頭先簡介autohotkey內所用的函數RegExMatch,用法是
FoundPos := RegExMatch(Haystack, NeedleRegEx [, UnquotedOutputVar = "", StartingPosition = 1])
    • 一定要有的參數:Haystack是被搜尋的文字,Needle則是所搜尋的pattern,也就是底下要學的東西。
    • 選擇性參數:match後的結果會儲存在OutputVar變數裡,StartingPosition可以指定從Haystack的第幾個字元開始搜尋。
    • 這個函數會傳回第一個找到pattern的字元位置,如果沒找到的話是 0 。
    • 相較於autohotkey很多函數/變數是大小寫不區分,regular expression預設是區分大小寫的。

Regular Expression

  • 一般字元
    • 大部分的字元都可以直接用在regular expression內,搜尋起來就是和傳統的搜尋文字一樣,例如 RegExMatch("abcde","bcd") 會傳回 2 。
  • 特殊字元
    • 在regular expression裡有特殊意義,要用 \ 去escape,才會是原本的字元:\ . * ? + [ { | ( ) ^ $ 
    • 例如要找中括號,不能用 RegExMatch("()[]{}","[") ,而是要用 RegExMatch("()[]{}","\[") 
  • Symbols
    • .:一個點表示除了斷行以外的任何一個字元。
    • \d:表示任何一個數字,也就是0到9。如果要表達「一個非數字」,則用\D (大寫)。
    • \w:表示任何一個文字,包括數字、大小寫的a到z,還有底線 _ 。如果要表達「一個非文字」,則用\W (大寫)。
    • \s:表示某種空白字元,包括空白、tab (\t),斷行 (\r\n)等。如果要表達「一個非空白」,則用\S (大寫)。
    • [...]:一對中括號內為候選字,正面表列
      • [abc]:找某一個字元,可以是a,也可以是b或是c。
      • [a-z]:找某一個字元,可以是a到z。同理可以用在[A-Z][0-9],或是組合用[b-s4-7]
      • 候選字中某些特殊字元可以不用escape,例如 * ? + 都不用加 \ ,當然如果怕搞混加上去也沒關係。
      • [^...]:最前面標一個^表示是黑名單,找一個字元且這個字元不在黑名單中。
  • 數量詞
    • 前面的Symbol都是指單一字元,數量詞一定接在某個symbol右邊,也就是說數量詞作用在左邊那一個symbol。
    • *:表示0個或是0個以上。
    • ?:表示0個或是1個。
    • +:表示1個或是1個以上。
    • {min,max}:表示min個以上、max個以下。例如a{2,3}表示連在一起的兩個或三個a。
  • Syntax
    • \b:表示一個「文字邊界」,例如 cat\b 不會match到 catfish ,因為cat是fish連在一起的。\B(大寫)則表示「非文字邊界」。
    • ^ $:把^放在整個pattern的最前面表示「開頭」,把$放在整個pattern的最後面表示「結尾」,例如 ^bcd abc$ 都無法match到 abcd
    • |:表是「或」,例如 (Sun|Mon|Tues|Wednes|Thurs|Fri|Satur)day 可以match到 SundayMonday 或 ... 或 Saturday
    • ( ):小括號一方面可以讓pattern比較容易閱讀,一方面被括起來的部分會成為一個submatch。
  • Options
    • 在pattern的左邊,以一個右括號)隔開option和pattern,各個options可以連起來寫,或是用空白、tab分開。
    • i:表示case insensitive,例如 i)abc 可以match到 ABC
    • m:表示multiline模式,影響的是^$,在多行文字時本來表示「全部文字」的頭尾,會變成「一行文字」的頭尾。
    • s:讓一個點.表示「所有」字元,包括斷行。
    • U:ungreedy
      • 預設情況下如果是不定數量的數量詞* + ? { },regular expression會符合「盡量多」的字元,所以 1\d+1 會match到全部的 1000010000101,稱作greedy
      • 如果在數量詞後加一個問號?,則變成ungreedy1\d+?1 只會match到 100001。
      • 如果有U設定,則預設為ungreedy,加問號變greedy。
  • Submatch (subpattern)
    • 剛才提到小括號可以形成submatch,例如 RegExMatch("zzabcdefzz","ab(.*)e(f)",match) match變數會儲存 abcdefmatch1變數會儲存 cdmatch2變數會儲存 f
    • 如果小括號只是為了語法,而不是為了submatch,可以用(?:  )表示這個括號不是submatch,例如 RegExMatch("zzabcdefzz","ab(?:.*)e(f)",match) match變數會儲存 abcdefmatch1變數會儲存 f
    • (<?P<...>   ):Named subpattern,例如 RegExMatch("20140102","(?P<Year>\d{4})(?P<Month>\d{2})(?P<Day>\d{2})",match) 執行後,match20140101,而matchYear變數是2014matchMonth變數是01matchDay變數是02
  • Assertion
    • 雖然是用小括號,但不是submatch也不包含在match裡,僅僅是當成一個條件。
    • (?=   ):positive look ahead,例如 a(?=b) 會match一個a,這個a要接著b。
    • (?!   ):negative look ahead,例如 a(?!b) 會match一個a,這個a後面不能接著b。
    • (?<=  ):positive look behind,例如 (?<=a)b 會match一個b,這b左邊接著a。
    • (?<!  ):negative look behind,例如 (?<!a)b 會match一個b,這b左邊不能接著a。

Further Reading

沒有留言:

張貼留言