1. 程式人生 > >正則學習二三事

正則學習二三事

正則一直是我一大痛點,一直都想解決這個問題,但是奈何每次看到那麼多符號就發矇,所以就一直拖下去了。直到最近總是被別人問到如何在hql中使用rlike查詢符合特定規則的欄位,然後各種不會,結果被鄙視的一塌糊塗,無奈,這才靜下心來慢慢研究。
以前看正則就是一堆符號,代表各個意思,如\d表示數字,\d+表示一個或者多個連續數字,單看每個規則都可以理解,除了組合,但是實際使用時真的很難組織到一起。歸根結底還是因為對這些符號的理解不夠深入。所以正則還是得多寫,推薦一個線上練習的網站RegexGolf。好了,下面寫寫自己學習正則的一些總結吧,希望能夠幫助到別人,也幫助自己總結總結。

正則的基礎知識:

  • 字面值
    正則表示式由只代表自身的字面值和代表特定含義的元字元組成。 只代表自身的字面值指的是普通的字元,如abcde,特殊含義的元字元包括:

    字元 含義
    \ 反斜線字元
    \0n 帶有八進位制值 0 的字元 n (0 <= n <= 7)
    \0nn 帶有八進位制值 0 的字元 nn (0 <= n <= 7)
    \0mnn 帶有八進位制值 0 的字元 mnn(0 <= m <= 3、0 <= n <= 7)
    \xhh 帶有十六進位制值 0x 的字元 hh
    \uhhhh 帶有十六進位制值 0x 的字元 hhhh
    \t 製表符 (‘\u0009’)
    \n 新行(換行)符 (‘\u000A’)
    \r 回車符 (‘\u000D’)
    \f 換頁符 (‘\u000C’)
    \a 報警 (bell) 符 (‘\u0007’)
    \e 轉義符 (‘\u001B’)
    \cx 對應於 x 的控制符
  • 字元類
    字元類是字元在方括號中的集合。表示“找到集合裡任意一個字元“。例如:

    字元 含義
    [abc] a、b 或 c(簡單類)
    [^abc] 任何字元,除了 a、b 或 c(否定)
    [a-zA-Z] a 到 z 或 A 到 Z,兩頭的字母包括在內(範圍)
    [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](並集)
    [a-z&&[def]] d、e 或 f(交集)
    [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](減去)
    [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](減去)

    從上表可以看出[]裡面可進行並集/交集/差集操作。對於字元範圍是根據ASCII值的大小來的,例如[A-z]也是可以的,甚至能夠匹配[,但是完全不建議如此使用,推薦使用的字元範圍:[0-9]/[a-z]/[A-Z]。
    字元類還有一些預定義的字元類:

    字元 含義
    . 任何字元(與行結束符可能匹配也可能不匹配)
    \d 數字:[0-9]
    \D 非數字: [^0-9]
    \s 空白字元:[ \t\n\x0B\f\r]
    \S 非空白字元:[^\s]
    \w 單詞字元:[a-zA-Z_0-9]
    \W 非單詞字元:[^\w]

    使用上面的預定義字元類能夠更加方便的表示字元範圍。需要牢記。
    注意: 區間是字元的區間,不是數字的區間。正則表示式[1-31]表示“找到一個1或一個2或一個3”,不是“找到一個從1到31的整數”。

  • 乘法器

    字元 含義
    X? X,一次或一次也沒有
    X* X,零次或多次
    X+ X,一次或多次
    X{n} X,恰好 n 次
    X{n,} X,至少 n 次
    X{n,m} X,至少 n 次,但是不超過 m 次

    X可以使一個普通字面值,如a+:一個或多個a,也可以是一個字元類,如[abc]{2},表示a/b/c後跟a/b/c。
    值得注意的是優先選擇更長的匹配,因為乘法器是貪婪的。如果你輸入的文字是I had an aaaaawful day,該正則表示式就會在aaaaawful中匹配到aaaaa。不會在第三個a後就停止匹配。
    乘法器是貪婪的,但它不會忽略一個更好的匹配。如果你的輸入文字為I had an aaawful daaaaay,之後這個正則表示式會在第一次的匹配中於aaawful找到aaa。只有在你說“給我找到另一個匹配”的時候,它才會繼續搜尋然後在daaaaay中找到aaaaa。
    惰性:
    正則表示式“.表示“找到一個雙引號,接著找到儘可能多的字元,最後再找到一個雙引號”。注意一下被.匹配的內部字元,很可能包含多個雙引號。這通常不是非常有用。乘法器可通過追加問號(?)來實現惰性“.?”*表示“匹配一個雙引號,跟著一個儘可能少的字元,再跟著一個雙引號”。

  • 分支
    可以使用管道符號來實現匹配多種選擇。

    字元 含義
    X|Y X或者Y
  • 組合
    可以使用圓括號來組合表示式。例:
    在一週中找到一天,使用(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day,這裡如果把小括號或者中括號,結果是完全不一樣的,因為中括號是字元類,即裡面的Mon並不是完全匹配Mon,而是隻要匹配M/o/n其中一個即可。
    同時組合後面還可跟上乘法器,如:\w+(\s+\w+)*代表“找到一個或多個單詞,它們以空格隔開”。

  • 邊界
    邊界分成:單詞邊界,行邊界,文字邊界

    1. 單詞邊界
      單詞邊界是一個單詞字元和非單詞字元之間的位置。記住,一個單詞字元是\w,它是[0-9A-Za-z_],一個非單詞字元是\W,也就是[^0-9A-Za-z_]。
      文字的開頭和結尾總是當作單詞邊界。
      輸入的文字it’s a cat有八個單詞邊界,分別為:文字開頭-i,t-‘,’-s,s-空格,空格-a,a-空格,空格-c,t-文字結尾。
    2. 行邊界
      每一塊文字會分解成一個或多個行,用換行符分隔。
      注意文字不是以換行符結束,而是以行結束。然而,任何行,包括最後一行,可以包含零個字元。
      起始行位置是在一個換行符和下一行的第一個字元之間。與單詞邊界一樣,在文字的開頭也算作一個起始的行。結束行位置是在行的最後一個字元和換行符之間。與單詞邊界一樣,文字結束也算作行結束。
    3. 文字邊界
      很多實現提供一個標記,通過改變它來改變^和$的含義。從“行開始”和“行結束”變成“文字開始”和“文字結束”。其它的一些實現提供單獨的元字元\A和\z來達到這個目的。

    一些表示邊界的符號:

    字元 含義
    ^ 行的開頭
    $ 行的結尾
    \b 單詞邊界
    \B 非單詞邊界
    \A 輸入的開頭
    \G 上一個匹配的結尾
    \Z 輸入的結尾,僅用於最後的結束符(如果有的話)
    \z 輸入的結尾

    其中^$是最常用的兩個邊界分隔符。

捕獲和替換:

  1. 捕獲組
    ()在正則中被用來表示組,同時也可以用來捕獲匹配上的子串,可以擁有多個捕獲組,它們甚至可以巢狀使用,捕獲組從左到右進行編號,只要計算左圓括號。例如:
    對於表示式:(\w+) had a ((\w+) \w+),文字是I had a nice day,那麼

    • 捕獲組1是I。
    • 捕獲組2是nice day。
    • 捕獲組3是nice。
    • 捕獲組0是I had a nice day(根據具體實現不同)

    如果表示式使用了兩個捕獲組,但是隻捕獲到一組,那麼組2是空字串。引用捕獲組使用+組序號,如\1表示引用第一個捕獲組。

  2. 後向引用
    可以在同樣的表示式中引用同一個捕獲組,這稱為後向引用。
    例:表示式[abc]{2}表示“匹配aa或ab或ac or ba或bb或bc或ca或cb或cc”,但是表示式([abc])\1表示“匹配aa或bb或cc”。

以上就是正則的全部知識,其實瞭解正則的知識點很簡單,但是真要應用到實際中還是需要通過大量的練習才能做到熟練使用。

實際案例
  1. 壓縮CSS檔案,去掉CSS檔案中的換行以及空格
工具:Notepad++  查詢:([{;])\s+  替換:\1  

以上案例會不斷更新,已記錄一些自己對正則使用的經歷。

參考文章