跳至主要内容

正規表達式

正規表達式

Regular Expression 從入門到入土


放在前面

Regex

  • 適用於: 所有需要字串驗證、比對、搜尋、擷取等場景
  • 不適用於: 長文匹配(效能問題) 不支援環境

工具


引入

各位是否遇過需要搜尋特定字串的情況? 假定要從Youtube網址中擷取出影片ID(v=xxxxxxxx) 在輸出3HDP4xp8Oj4的前提下,各位會如何做呢?

https://www.youtube.com/watch?v=3HDP4xp8Oj4&feature=youtu.be

使用String.split? 使用URLSearchParams?

或者... 使用正規表達式!?


測試網址


規則

  • \
  • ?、+、*、{}
  • ()、|
  • []
  • ^、$、\b
  • (?=)
  • flag

字元匹配

在Regex內的一般文字將直接配對

/abc/ // 匹配abc

"123abc456".match(/abc/)
// ['abc', index: 3, input: '123abc456', groups: undefined]

「.」 可以匹配\r\n(換行字元)以外任何字元

/tr.../ // tr以及任意三個字元

"trick or treat".match(/tr.../g)
// ['trick', 'treat']

集合

[]中括號內的字元, 會以「或」的形式當成一個普通字元配對

/[早午晚]安/ // 早安 or 午安 or 晚安

"早安 午安 晚安 平安".match(/[早午晚]安/g)
// ['早安', '午安', '晚安']

[]內可以使用「-」表示連續的「或」

/[0-9A-Za-z]/ // 數字+英文字母大小寫

"59分 87分 8a分".match(/[6-9][0-9]分/g)
// ['87分']

反集合

[]的開頭加入跳脫字元「^」 代表除了涵蓋內的字元都可以配對 同樣可以使用「-」表示連續排除

/[^abc123ABC]/

// 排除小寫a-z與空格
"Apple book Cat Docker".match(/[^a-z ]../g)
// ['App', 'Cat', 'Doc']

量詞

{}量詞均代表前面的表達式重複次數

  • {x} 剛好出現x次
  • {x,}至少出現x次至多無限
  • {x,y}至少出現x次,且至多只能出現y次, 同時x<=y (否則報錯)
  • ⚠特別注意,xy只能是數字, 並且,間不能有空格

想想看如果放入非數字,或者加空格會發生什麼? 點我看答案


量詞-縮寫

  • ?代表前面的表達式可有可無 (0~1次) = {0,1}
  • +代表前面的表達式至少一次 (1~inf次) = {1,}
  • *代表前面的表達式隨意出現 (0~inf次) = {0,}
/https?:\/{2}example.com/ /*匹配example.com,且https或http皆可*/
/[0-9]+.[0-9]+/ /*匹配浮點數(有潛在問題)*/
/.*/ /*隨意匹配*/

想一下第二個範例的浮點數潛在什麼問題? 點我看答案


非貪心量化

詳細介紹 Regular expression: 貪婪、非貪婪 簡單來講,在量詞後面加上?會盡可能減少配對

re  = /阿.+是蟑螂!*/
re2 = /阿.+?是蟑螂!*/
str = "阿啊啊啊是蟑螂啊啊啊幹幹幹幹幹幹是蟑螂!!!!ldsjghiwjp"
/* re <------------------------------------>
re2 <---------->
貪婪與非貪婪的配對數量差距
*/

貪婪效能測試


特殊字元「\」

反斜線\可以將字元轉譯, 包含轉譯成特殊字元(bBdDfnrsStuvwWx), 以及轉譯回普通字元(^$()*+?.[\{|)

  • \f匹配分頁符號(form-feed)
  • \n匹配換行(line-feed)
  • \r匹配回車(return)
  • \t匹配製表符(horizontal tab)
  • \v匹配垂直製表符(vertical tab)
  • \x--以16進位的ASCII code匹配 (00~ff)
  • \u----以10進位unicode匹配 (0000~9999)

特殊字元-縮寫

符號等效縮寫匹配內容
\d[0-9]數字(*註1)
\D[^0-9]非數字
\s[\r\n\t\f\v ]空格、換行等(*註2)
\S[^\r\n\t\f\v ]非空格、換行等
\w[A-Za-z0-9_]單字字元(*註3)
\W[^A-Za-z0-9_]非單字字元

以下狀況於Unicode的Regex出現

  • 註1: \d\D 會符合/排除全形數字字元
  • 註2: \s\S 會符合/排除全形、特殊空格
  • 註3: \w\W 會符合/排除各國文字

群組

()小括號有三個功能

  1. 提高匹配的優先順序
  2. 把一段表達式包裹起來(通常用於量化)
  3. 將括號內匹配到的擷取為群組輸出
"abc".match(/([a-z]+)/)[1] // abc
"abc".match(/[a-z]+/)[1] // undefined

|將表達式分隔成左右兩段,以「或」的形式匹配 如果未放置在括號內,則作用在全域

"fool".match(/food|l/) // l
"fool".match(/foo(d|l)/) // fool

命名/非捕捉 群組

(?<name>)在括號開頭加上?<>, 同時在<>內輸入文字即可為群組命名 (?:)如果不需要括號的捕捉功能, 可在括號開頭加上?:

"25歲".match(/(?<age>\d+)/).groups?.age // 25
"25歲".match(/(\d+)/).groups?.age // undefined

"abc".match(/([a-z]+)/)[1] // abc
"abc".match(/(?:[a-z]+)/)[1] // undefined

向後參考

()\num當群組捕捉到文字時, 可在其後方使用向後參考匹配相同的文字 \num意思為第num個被捕捉到的文字

"一心一意".match(/(.).\1./) // 一心一意
"一石二鳥".match(/(.).\1./) // null

定位匹配

  • ^匹配至字串開頭
  • $匹配至字串結尾
  • \b匹配至單字邊界
  • \B匹配非單字邊界
"123".match(/^\d+$/) // 123
"a123".match(/^\d+$/) // null

(/er\b/).test("never") // true
(/er\b/).test("version") // false

向前/後看

「在一個字串中找出連續數字 6~8 個」 "12345 XD Hi12345678ab666666cd987654321" 只能輸出:12345678 和 666666 思考一下如何解決? 點我看解答


XY分別為一段表達式, 在表達式X的前/後加入(?=)(?!), 可以指定表達式X前/後方須出現的表達式Y。 ※如果Y放在X前方, 則?的後方需要多一個小於(<)符號


ahead與behind可以合併使用, 且不計入匹配範圍

  • look ahead
    • Positive: X(?=Y)
    • Negative: X(?!Y)
  • look behind
    • Positive: (?<=Y)X
    • Negative: (?<!Y)X

RegExp 應用: lookahead , lookbehind


優先級

優先權符號
最高\
()(?:)(?=)[]
量詞 *+?{n}
^$、中介字元
次低相鄰字元
最低\|

Flag

flag通常放置於雙斜線後方

/[\w\W]*/gmiu
符號全稱含義
gGlobal匹配所有可能
mMulti line^$可以匹配\r\n
iInsensitive大小寫不敏感
uUnicode以unicode進行匹配

練習

嘗試做做看IP驗證吧


結語

雖然Regex可以做的事很多, 但通常善用第三方lib會是更好的選擇。 例如時間驗證等等


參考


End