正規表達式語法參考
訂閱 375
本節講述的正規表達式符合 PCRE 標準,本節展示的範例均為 JavaScript 程式碼。
正規表達式
正規表達式也被稱為正規表示式,正規表示法,他提供了一套強大而有效的規則,用於目標字串的比對和取代,相比於簡單的字串操作,正規表達式可以完成更加複雜的工作和目標。
PCRE 標準和 POSIX 標準
正規表達式存在 PCRE 和 POSIX 兩種標準,前者提供了更多的功能和更為簡潔的語法,因而在程式設計語言中獲得了普遍的支援。
正規表達式中的跳脫字元
在正規表達式中,\
被視為跳脫字元,他與其後的一個或多個字元組合,用來表示某種特殊的含義,功能或某個字元。這對於上下文中的特殊字元是非常必要的,因為他們需要跳脫才能表達自身,比如,表示開始位置的^
需要使用\^
表達自身,表示結束位置的$
需要使用\$
表達自身。
跳脫
想要詳細了解跳脫,你可以檢視跳脫,跳脫字元,跳脫序列介紹一節。
使用正規表達式比對字元
要想使用正規表達式比對具體的字元,只需直接書寫這些字元即可,先決條件是他們不必跳脫。比如,正規表達式he
與字串'a hero'
中的he
相符。
當然,除了簡單比對,PCRE 還支援以下的字元比對方式。
- .
.
用於比對任何一個非換行字元和歸位字元的字元。- \n
\n
用於比對一個換行字元。- \r
\r
用於比對一個歸位字元。- \t
\t
用於比對一個定位字元。- \v
\v
用於比對一個垂直定位字元。- \f
\f
用於比對一個分頁字元。- \w
\w
用於比對一個英文字母,數值(0
至9
)或底線(_
)字元。- \W
\W
與\w
相反,比對任何一個非英文字母,數值或底線的字元。- \d
\d
用於比對一個數值(0
至9
)字元。- \D
\D
與\d
相反,比對任何一個非數值字元。- \s
\s
用於比對一個空白字元(包括空格字元,換行字元,定位字元等)。- \S
\S
與\s
相反,比對任何一個非空白字元。- \xHH
\x
後面跟隨一個兩位的十六進位數值,用於跳脫為對應的字元,比如,\x0A
對應了換行字元。- \uHHHH
\u
後面跟隨一個四位的十六進位數值,用於跳脫為對應的字元,比如,\u000D
對應了歸位字元。- \OOO
\
後面跟隨一個三位以內的八進位數值,用於跳脫為對應的字元,先決條件是之前不存在對應個數的擷取組,比如,\15
對應了歸位字元,當\15
之前不存在 15 個擷取組時。- \cX
\c
後面跟隨一個英文字母,用於比對Control和對應字母的組合操作,比如,\cJ
表示Control和J
的組合操作,操作產生的效果需要視具體環境而定。
正規表達式\d\d
比對兩個相鄰的數值字元,在字串'他們的年齡為 12,8,13,102'
中,8
只有一個數值字元,因此不符合要求,102
中的10
是兩個相鄰的數值字元,符合要求,但末尾剩下的2
不再符合。
'他們的年齡為 12,8,13,102'.match(/\d\d/g)
// 比對結果:['12', '13', '10']
正規表達式.\s.
比對兩個被空白字元分隔的非換行字元和歸位字元的字元。
'你好 嗎?\r\n 不好!'.match(/.\s./g)
// 比對結果:['好 嗎']
正規表達式\w\w\w\w
比對四個字元的英文字母,數值,底線(或他們之間的任意組合)。
'I like this c__, what ab__t you?'.match(/\w\w\w\w/g)
// 比對結果:['like', 'this', 'what', 'ab__']
使用正規表達式比對字元集
在正規表達式中,可以使用[]
來比對一個屬於指定集合的字元,只需要在括弧中包含相應字元或跳脫序列即可。如果希望實作相反的效果,則需要在[]
中的第一個位置加入^
,以表示比對一個不屬於指定集合的字元。
需要特別指出的是,一些在[]
外起到特殊作用的字元,比如.
,$
,+
,?
,*
,在[]
內會表示其原本含義,一些在[]
外可以使用的跳脫序列,比如\d
,\w
,在[]
內依然有效。
正規表達式li[kv*?]e
比對前兩個字元為li
,第四個字元為e
,第三個字元屬於kv*?
之一的字元組合。
'like live li*e libe lie'.match(/li[kv*?]e/g)
// 比對結果:['like', 'live', 'li*e']
如何使用正規表達式比對特定範圍的字母或數值?
在正規表達式的[]
中,可使用-
來表示某個範圍內的英文字母或數值,比如,A-Z
表示大寫字母A
到Z
,b-q
表示小寫字母b
到q
,0-7
表示數值0
到7
。
當你希望在[]
中表示-
自身時,最好采用\-
進行跳脫。
正規表達式li[^c-z*]e
比對前兩個字元為li
,第四個字元為e
,第三個字元不屬於*
以及c
到z
的字元序列。
'like live li*e libe lie'.match(/li[^c-z*]e/g)
// 比對結果:['libe']
如何使用正規表達式比對退格字元?
在大部分語言的字串中,退格字元需要使用\b
進行跳脫,這樣的做法在正規表達式中是不可行的,因為\b
具有其他含義,想要比對退格字元可以采用替代方案,比如[\b]
,[]
中的\b
將跳脫為退格字元,而不是像\d
或\w
一樣保持[]
內外跳脫的一致性。
在正規表達式中擷取比對內容
在正規表達式中,可使用()
建置一個擷取組(Capture Group),括弧中需要包含一個子運算式,比如,I (like) cats
。擷取組一般用於提高運算的優先順序,字串的取代,作為被參考的目標。
如果你僅僅把擷取組中的內容視為一個整體,並不準備進行取代和參考,那麽可將其轉換為(?:)
表示的非擷取組。
如何在正規表達式中參考已比對內容?
當你在正規表達式中定義一個擷取組之後,即可使用跳脫序列\NUM
向後參考(Back Reference)該擷取組,其中NUM
為擷取組的序號。擷取組序號一般會從1
開始,並按照左括弧出現的順序依次遞增1
,這一點對於計算巢狀擷取組的序號非常有效。
正規表達式([%#])[^%#]*\1
擁有一個擷取組([%#])
,他與字元%
或#
相符。\1
參考了該擷取組,因此整個運算式將用於比對被一對%
或#
括住的內容。
'%晚餐好了嗎?%#還是出去吃吧#%不錯的主意#'.match(/([%#])[^%#]*\1/g)
// 比對結果:['%晚餐好了嗎?%', '#還是出去吃吧#']
下面,我們做出一些調整,將之前的([%#])
改為巢狀擷取組(:([%#]))
,將之前的\1
改為\2
以保持正確的參考。
':%晚餐好了嗎?%#還是出去吃吧#'.match(/(:([%#]))[^%#]*\2/g)
// 比對結果:[':%晚餐好了嗎?%']
如何在 JavaScript 的 replace 方法中參考已比對內容?
JavaScript 的replace
方法支援正規表達式,如果其第二個參數為字串,你可以在該字串中使用$
和擷取組序號來參考擷取組。
正規表達式:([%#])([^%#]*)(\1)
比對以:
開始並被一對%
或#
括住的內容,replace
方法將取代該內容中的英文:
,%
或#
。
'他說:%晚餐好了嗎?%'.replace(/:([%#])([^%#]*)(\1)/g, ':「$2」')
// 取代結果:'他說:「晚餐好了嗎?」'
使用正規表達式比對位置
在正規表達式中,你可以使用斷言來比對一個具體的位置,斷言不會消耗字元,這意味著被用於斷言的字元可以繼續在正規表達式中使用。
- \b
\b
用於比對\w
和\W
之間的界限,這對於確定英文單字通常是有效的。- \B
\B
與\b
相反,比對兩個\w
或兩個\W
之間的界限。- ^
^
用於比對字串的開始位置,如果開啟了多行模式,他還會比對\r
或\n
後面的位置。- $
$
用於比對字串的結束位置,如果開啟了多行模式,他還會比對\r
或\n
前面的位置。- (?<=PATTERN)
正向後行斷言(Positive Lookbehind Assertion)用於比對一個位置,該位置之前的內容符合子運算式
PATTERN
的要求。- (?<!PATTERN)
負向後行斷言(Negative Lookbehind Assertion)用於比對一個位置,該位置之前的內容不符合子運算式
PATTERN
的要求。- (?=PATTERN)
正向先行斷言(Positive Lookahead Assertion)用於比對一個位置,該位置之後的內容符合子運算式
PATTERN
的要求。- (?!PATTERN)
負向先行斷言(Negative Lookahead Assertion)用於比對一個位置,該位置之後的內容不符合子運算式
PATTERN
的要求。
後行和先行斷言應位於正規表達式的開始或結尾位置
正向後行斷言和負向後行斷言應位於正規表達式或子運算式的開始位置,正向先行斷言和負向先行斷言應位於正規表達式或子運算式的結尾位置,否則運算式可能無法有效工作。
正規表達式\blive\B
用於比對作為單字開頭出現的live
。
'I live here'.match(/\blive\B/g)
// 比對結果:null
正規表達式[a-zA-Z]+\b(?! 7)
使用了負向先行斷言,用於比對字串中包含任何非7
版本的產品。
'iPad 7 iPhone 11 iPad 9 Windows 7 iPhone 7'.match(/[a-zA-Z]+\b(?! 7)/g)
// 比對結果:['iPhone', 'iPad']
在多行模式下,正規表達式(?<=^The\s)\w+
與字串開始處的apple
,以及\n
開始處的orange
相符。
'The apple is...\r\nThe orange is...'.match(/(?<=^The\s)\w+/gm)
// 比對結果:['apple', 'orange']
在正規表達式中設定比對次數
你可以在正規表達式中使用數量詞來設定比對的次數,而不是重複的書寫某些內容,數量詞可被用於運算式中的大部分目標,包括擷取組,字元集等。
- *
*
表示比對 0 次或更多次。- +
+
表示比對 1 次或更多次。- ?
?
表示比對 0 次或 1 次。- {n}
{n}
表示比對n
(n
大於等於0
)次。- {n,}
{n,}
表示比對n
(n
大於等於0
)次或更多次。- {n,m}
{n,m}
表示比對次數可以大於等於n
(n
大於等於0
),小於等於m
(m
大於等於n
)。
正規表達式\bwo{2,3}\b
比對以w
開頭並含有2
到3
個o
的單字。
'wo woo wooo woooo'.match(/\bwo{2,3}\b/g)
// 比對結果:['woo', 'wooo']
正規表達式(-?foo){2,}
比對至少含有兩個foo
的字元組合。
'foo foo-foo foo-foo-foo'.match(/(-?foo){2,}/g)
// 比對結果:['foo-foo', 'foo-foo-foo']
如何在正規表達式中盡可能減少比對次數?
正規表達式中的數量詞預設為貪心模式,他會盡可能多的進行比對,比如,wo*
與字串'woooooooho'
中的wooooooo
相符。如果希望盡可能少的比對,你需要使用非貪心模式,在相關數量詞後加上?
,比如,wo*?
與字串'woooooooho'
中的w
相符。
符合多個正規表達式中的任意一個
你可以使用|
列出多個正規表達式,他們會按照從左到右的順序進行計算,當某個正規表達式比對成功時,剩余的運算式將被忽略。
正規表達式I (?:love|like) you
中的非擷取組(?:love|like)
,將與love
或like
相符。
'I love you, I hate you'.match(/I (?:love|like) you/g)
// 比對結果:['I love you']
正規表達式中的運算優先順序
正規表達式中的各種符號,存在以下遞減排列的優先順序關系。
- \
跳脫字元
\
的優先順序最高。- (),[]
表示擷取組,非擷取組,斷言的
()
,以及表示字元集的[]
的優先順序較高。- *,+,?,{n},{n,},{n,m}
數量詞具有中等優先順序。
- ^,$,字元
表示位置的
^
和$
,以及字元的優先順序較低。- |
組合多個正規表達式的
|
的優先順序最低。
正規表達式Yes\.|No\b.*?\bbecause\b\s+([^.]+)\.
將比對Yes
或給出原因的No
。
'No, because nothing.Yes.No'.match(/Yes\.|No\b.*?\bbecause\b\s+([^.]+)\./g)
// 比對結果:['No, because nothing.', 'Yes.']