這是一篇針對正則表達式的完整介紹,將會分爲兩個部分,第一部分包含平常使用正則會高頻率用到的正則語法,第二部分包含一些進階語法。正則表達式
不少軟件都提供了文本的查找功能,一般使用 Ctrl+F/Command+F 快捷鍵來使用,可是通常這些軟件提供的文本查找功能都很是基礎,只能進行全字符匹配,好比輸入「Hello」就只能匹配到「Hello」,而要按照一些特定規則查找的話,就沒法實現了。正則表達式是一個用來查找匹配特定文本的工具,在軟件開發過程當中會常用到,它能夠按照特定的規則進行文本的查找匹配。編程
正則一般使用一對斜槓 /
來標記,好比 /^Hello World$/
,可是並非全部地方都是使用 /
來標記的,只不過對於絕大多數編程語言來講是這樣的,或者說,先後兩個 /
並不屬於正則,只有中間的部分纔是。本文也將採起通常的形式,使用 /
來標記正則表達式。json
正則做爲一個「高級」文本查找工具,對於通常軟件的基本全字匹配功能確定是涵蓋在內的。要實現普通的全字匹配,只須要和普通的查找同樣,直接寫出要匹配的字符串便可,可是因爲正則表達式有一些特殊的語法,所以有些符號是有特殊用處的,包括:\
、^
、$
、|
、.
、+
、*
、{
、?
、(
、)
、[
等,這些特殊符號的含義將在後面講到,對於這些特殊符號,若是須要用到的話,須要在前面添加 \
進行轉義。數組
示例:編程語言
/Hello World/
匹配Hello World
工具
全字匹配的時候,有時咱們要限制匹配結果所在的位置,好比,當你要在一堆手機號中查找 130
號段的號碼,此時若是使用全字匹配,你可能會匹配到 18712341301
這樣的結果,由於其中包含了 130
這個子串,可是咱們須要的是 130
開頭的號碼,因此全字匹配不能知足需求了。此時,只要在全字匹配的正則中添加一個特殊符號 ^
,這個特殊符號僅僅做爲一個位置標記符,表示整個要匹配的目標字符串的開頭,要注意這一點,它是一個表示開始位置的佔位符。spa
示例:code
/^130/
僅匹配處於字符串開頭位置的130
,好比13012345678
,而相似於11302345678
這種雖然包含130
,可是不是在最開頭包含的,則會匹配失敗。three
與 ^
匹配開始位置對應的是 $
,表示要匹配的目標字符串的結尾,它也僅僅是一個表示位置的佔位符。ip
示例:
/ed$/
僅匹配處於字符串結尾的ed
,好比opened
、closed
,而相似於edge
、bedroom
這些雖然包含ed
,可是不在結尾的,則會匹配失敗。
/^Hello World$/
僅匹配既處於開頭又處於結尾的Hello World
,所以只有一種狀況是知足的:也就是整個匹配的目標字符串就只有Hello World
。
有時,咱們須要的匹配的內容並不直接肯定,可是要求必定屬於某個集合內。好比要匹配性別:男
、女
,咱們就不能直接使用全字匹配了。此時,咱們可使用一個特殊符號 |
,表示「或者」。這樣咱們就能夠將全部須要匹配的元素集合依次列出來,使用 |
分隔便可。要注意的是,匹配結果必定是二選一。
示例:
/男|女/
能夠匹配到一個字符串中包含的男
或者女
,好比:性別:男
,而若是目標字符串中即不包含男
也不包含女
,則匹配失敗。
/^男|女$/
僅當目標字符串只有一個字「男」或者只有一個字「女」的時候能夠匹配成功。而相似於男女
這樣兩個字,雖然既包含了男
也包含了女
,其中男
處於開頭位置,女
處於結尾位置,可是並不存在「既處於開頭又處於結尾的」男
或女
,因此是匹配失敗的。
/one|two|three|four|five/
匹配 1~5 的英文數。因而可知,你能夠聽任意多個須要匹配的元素列表,匹配結果將是列表中的任意一個。
有了 |
,咱們能夠把想要匹配的子串所有列舉出來,但有時,你會發現這是個大工程:好比,咱們想要匹配手機號碼,在中國,手機號碼長度一般都是 11 位,而且開頭三位表示號段,有 130
、131
等(而且還在增長),後面跟着 8 位數字。如今想要匹配這樣的數字串,使用上面的方法就不太夠了,你也許須要把全部可能的手機號所有列出來?
不過正則裏有一個萬能字符,使用特殊符號 .
表示(就是小數點),這個符號能夠表示任意單個字符。
示例:
/^130........$/
能夠匹配130
開頭,後面跟着 8 位任意字符的字符串,好比13012345678
、130abcdefgh
。
實際上,上面這個匹配手機號的例子有點粗糙,它雖然能夠匹配到全部 130
開頭的手機號,但也能夠匹配到後面跟的不是數字的字符串,好比 130abcdefgh
,此時,咱們若是想要完完整整的匹配手機號,也就是後面 8 位要求必定是數字,這樣的話就不能用 .
了,由於它表示任意字符,而咱們只要 0~9 這十個字符。
正則表達式中存在一些預約義的「字符類」,好比這裏咱們只要匹配 0~9 這十個字符,在正則中可使用 \d
表示,它與 .
相似,表示一個字符,不同的是它僅僅表示 0~9 中的任意一個字符,有點相似於 /0|1|2|3|4|5|6|7|8|9/
。
示例:
/^130\d\d\d\d\d\d\d\d$/
能夠匹配130
開頭,後面跟着 8 位任意數字的字符串,好比13012345678
、13087654321
,而上個示例中的130abcdefgh
將再也不匹配,由於後面 8 位不是數字。
除了 \d
,正則中還有兩個比較經常使用的字符類:\w
和 \s
。其中 \w
在大部分正則引擎中表示 26 個大寫英文字母 + 26 個小寫英文字母 + 0~9 10 個數字 + 下劃線字符 _
,一共 63 個字符中的任意一個;而 \s
通常表示「空白字符」,好比 空格、
\t
製表符、\n
換行符、\f
換頁符等。
但值得注意的是,上面這三個字符類 \d
、\w
和 \s
具體包含哪些字符是根據不一樣的正則引擎實現而不一樣的!可是大致上來講,\d
就是數字,\w
是單詞符號、\s
是空白符號。
在一些特定的正則引擎實現中,還會包含一些其餘的字符類,具體要根據對應的正則引擎而決定。
有了 \d
、\w
、\s
三個字符類,正則還提供了他們的補集,使用他們的大寫來表示,好比 \D
表示全部不是數字的字符、\W
表示全部不是單詞符號的字符、\S
表示全部不是空白字符的字符。
雖說正則中提供了三個預約義的字符類,以及他們的補集,有的時候仍是不太夠用。好比,咱們要匹配十六進制數,十六進制數除了包含 0~9 十個數字字符外,還包含 a~f (不區分大小寫)六個(或者說十二個)英文字母,這樣一來簡單的 \d
就不夠了,\w
又包含多餘的字符會致使匹配的結果不許確。
正則提供了自定義字符類的方法,只要將所需的字符用中括號 []
括起來,便可造成一個自定義的字符類。
示例:
/^[0123456789abcdefABCDEF]$/
能夠匹配一位十六進制數。
像上面這樣把須要的字符列出來就能夠造成一個自定義字符類,可是當字符數比較多的時候,這樣全列出來就感受有點麻煩了。在自定義字符類中,可使用 -
符號來定義區間,能夠直接指定從一個起始字符到一個結束字符。因此上面的例子能夠改爲這樣:
示例:
/^[0-9a-fA-F]$/
能夠匹配一位十六進制數。
這樣是否是就好多了呢?固然也能夠指定更精細的區間,好比 /[0-37-9]/
表示 0、一、二、三、七、八、9 組成的字符類。
與正則預置的字符類同樣,自定義字符類一樣支持取補集,只要在自定義字符類的第一個位置放置一個 ^
符號(注意,這裏的 ^
再也不是字符串開頭的含義了),這樣就表示不包含後面列出的字符組成的字符類。
示例:
/^[^a-z]$/
匹配不是小寫英文字母的單個字符,好比 數字0
、大寫字母A
、特殊符號@
等。
在自定義字符類中,保留的特殊符號與第一節中說的那些不太同樣了,只有 ]
、-
、^
和 \
有特殊用途,做爲特殊符號,須要轉義,而其餘諸如 $
、|
之類的其餘符號在自定義字符類中都再也不須要轉義(固然,你想繼續轉義也是能夠的,可是會下降正則的可讀性)。
固然,這些特殊符號也能夠在他們沒有實際用途的時候,不進行轉義。這句話有點繞,具體來講,好比 -
,在自定義字符類中表示區間範圍,好比 /[a-c]/
表示 a
、b
和 c
組成的字符類,可是若是你將它放在字符類的開頭或者結尾,它將沒法構成範圍,好比 /[-a]/
或者 /[a-]/
就表示 a
和 -
組成的字符類。再好比 ^
放在自定義字符類的開頭表示補集,可是放在其餘位置就再也不有特殊意義,就不須要轉義了。而 \
做爲轉義符,自身永遠都須要被轉義 \\
。
還有 ]
,這個做爲一個定界符,表示自定義字符類的定義結束,可是若是將它放在自定義字符類的開頭,好比 /[]a]/
表示 ]
和 a
組成的字符類;或是放在第二個位置,而第一個位置是 ^
,好比 /[^]a/
表示不包含 ]
和 a
的字符類。可是注意,這在 JavaScript 中不適用!在 JavaScript 中,/[]/
不管什麼時候都表示一個永遠沒法匹配成功的空字符類,而 /[^]/
表示能夠匹配任意單個字符的字符類,因此在 JavaScript 中 ]
不管如何都有特殊意義,所以永遠都須要使用 \
進行轉義!
大多數在自定義字符類外面的轉義標記也能夠在自定義字符類中生效,好比不可打印字符(換行 \n
之類的)、八進制轉義符、十六進制轉義符、Unicode 轉義符。
示例:
/^[\^\]\-\\]$/
匹配^
、]
、-
或\
。
在部分正則引擎中(好比 .NET、XPath 等),自定義字符類還支持減法。好比 /[a-z-[aeiou]]/
表示匹配全部小寫的輔音英文字母,也就是從 a
到 z
這全部的 26 個英文字母中去除 a
、e
、i
、o
和 u
這五個元音字母。
還有部分正則引擎(好比 Java、Ruby 等),自定義字符類還支持交集。好比 /[a-z&&[^aeiou]]/
也表示匹配全部小寫的輔音英文字母,可是原理和上面不同,這個是要求字符既在 a
到 z
這 26 個英文字母之中,又不能在 a
、e
、i
、o
和 u
這五個緣由字母中。
到目前爲止,全部的正則表達式都是「靜態」的,也就是你寫了什麼就匹配到什麼,頂多使用字符類來代替多種字符。可是看看上面匹配手機號的示例,後面跟着 8 個 \d
,這很是尷尬,若是後面跟着更多的數字怎麼辦?
正則中提供了「重複」功能,能夠將前面的一個匹配內容重複屢次。使用特殊符號 +
或 *
,可使得前面一個匹配單元重複匹配屢次,其中 +
要求至少出現一次。
示例:
/^130\d+$/
能夠匹配130
開頭,後面至少跟一個數字的字符串,好比1301
、130123456789123456789
之類的,後面能夠跟任意多個數字,可是130
則不能匹配。
/^130\d*$/
能夠匹配130
開頭,後面跟不跟數字均可以,但要跟的話必須是跟數字的字符串,好比130
、1301
、130123456789123456789
,而130a
則沒法匹配。
⚠ 注意:重複是僅對前一個最小匹配單元生效的,上面的例子中,\d
是最小的匹配單元,因此是對 \d
重複。對於自定義字符類,也是屬於一個最小匹配單元。
+
和 *
的重複次數是上不封頂的,因此在匹配手機號的時候就用不到了,由於手機號後面固定是 8 位數字。此時,可使用更加靈活的重複控制方法:{m,n}
(中間不能有空格),這表示最少重複 m 次,最多重複 n 次(注意 m 和 n 都是閉區間)。
特別的,若是 m 與 n 相等的話,則能夠省略爲 {m}
,表示固定重複 m 次;而若是 n 等於正無窮的話,能夠省略 n 變爲 {m,}
,表示最少重複 m 次,上不封頂。
示例:
/^130\d{2,5}$/
能夠匹配130
開頭,後面跟着至少 2 個,至多 5 個數字的字符串,好比:13012
、130123
、1301234
、13012345
。
/^130\d{2,}$/
能夠匹配130
開頭,後面跟着至少 2 個數字的字符串,好比:13012
、13012345
、130123456789123456789
。
/^130\d{8}$/
能夠匹配130
開頭,後面跟着 8 個數字的字符串,好比:13012345678
。
因而可知,+
實際上就是 {1,}
的簡寫形式,而 *
實際上就是 {0,}
的簡寫形式。
還有一個特殊的「重複」類型:?
,這個實際上不算是「重複」了,可是屬於「重複」的衍生品,它表示不出現,或是出現一次,也就是 {0,1}
的簡寫形式。
示例:
/^colou?r$/
能夠匹配colour
或是color
。
/^https?:\/\/$/
能夠匹配http://
或是https://
。這裏/
雖然不是特殊符號,可是本文中使用/
來標記正則,因此爲了不/
被解析爲正則的邊界,因此使用\
對其進行了轉義。實際上若是你使用的正則引擎不是以/
來標記正則的,那麼這裏就不須要進行轉義,好比:|^https?://$|
。
在上面的重複中,咱們發現,重複只能對前一個最小匹配單元生效,若是咱們想要更靈活的重複怎麼辦呢?好比聖誕老人來了,HoHoHo~,這裏咱們想要匹配這個 HoHoHo
要怎麼辦呢?
正則中使用小括號 ()
進行分組,括號內能夠看做一個獨立的子正則表達式個體,整個括號將被做爲一個匹配單元對待,所以,咱們只要對一個 Ho
進行分組,而後重複對這個分組進行便可~
示例:
/^(Ho){3}~$/
能夠匹配HoHoHo~
。
聯繫到前面的「或者」,|
會對整個正則表達式生效,若是我有一個相似於這樣格式的數據:性別:%s,角色:%s
,其中性別只有 男
、女
兩種,角色有 管理員
、遊客
兩種,如今我想使用正則匹配這個字符串,簡單的使用 |
就辦不到了,此時就只有將先後兩個須要使用到 |
的部分分別進行分組。
示例:
/^性別:(男|女),角色:(管理員|遊客)$/
:匹配性別:男,角色:管理員
、性別:男,角色:遊客
、性別:女,角色:管理員
或性別:女,角色:遊客
。
到如今,咱們基本上能夠通吃絕大多數的狀況了。在有了分組以後,咱們能夠解鎖一個新的技能:引用一個分組。
正則的一個很是經常使用的功能就是分組引用。在上面的例子 /^性別:(男|女),角色:(管理員|遊客)$/
中咱們能夠看到有兩個分組,第一個是 (男|女)
,第二個是 (管理員|遊客)
,這樣在匹配的時候就能夠獲得兩個可使用的分組。最直觀的能夠體如今各個編程語言的匹配結果中,一般匹配結果會以一個數組形式(或者是類數組)返回,一般數組的第 0 個元素爲匹配到的整個字符子串,而從第 1 個元素開始,表示匹配到的第一個分組的內容,第 2 個元素表示匹配到的第二個分組的內容。
所以,字符串 "性別:男,角色:管理員"
使用 /^性別:(男|女),角色:(管理員|遊客)$/
匹配獲得的結果是:
[
"性別:男,角色:管理員",
"男",
"管理員"
]
複製代碼
分組除了能夠在匹配結果中使用外,還能夠在正則內部使用,好比,我要匹配使用一對引號引發來的字符串,引號能夠是英文單引號,也能夠是英文雙引號。好比 'hello'
、"world"
,若是咱們簡單的使用 /^['"]\w+['"]$/
,雖然能夠成功匹配這兩種狀況,可是對於 'hello"
、"world'
這種先後括號不匹配的狀況也能夠匹配成功,這顯然不是咱們想要的。這時,使用分組的引用就能夠解決這個問題:
示例:
/^(['"])\w+\1$/
能夠匹配由英文單引號或是英文雙引號引發來的單詞,而且確保先後的引號是匹配的。
這裏咱們爲 \w
前面的引號進行了一次分組,此時這個分組會被編號爲 1,這樣在後面咱們使用 \1
引用前面的這個分組,就能夠確保先後引號一致了~
這裏要注意一下八進制轉義符,八進制轉義符不太統一,但一般是 \
後直接跟數字,這就與分組引用衝突了,由於分組引用也是 \
後直接跟數字。因此通常建議是不要使用八進制轉義符,八進制一般能夠很容易轉成十六進制,所以建議在使用字符編號表示字符的時候,不要使用八進制,而是使用十六進制!
好比字母 'a'
,編號爲十進制爲 97,八進制爲 141,十六進制爲 61。那麼八進制表示爲 \141
,十六進制表示爲 \x61
,或者使用 Unicode 表示爲 \u0061
。