寫在開頭
在編碼過程中,正則表達式一直是經常會出現但又困擾我們的知識點,筆者就是每次遇到正則表達式就相當頭痛,往往都是從網上復制一個表達式到代碼里草草了事。但其實如果不真正理解正則表達式的含義,我們就無法對其進行修改來應對特殊的需求。所以本文選取幾個常用的正則表達式進行分析,旨在覆蓋基礎的正則表達式知識,讓和我一樣的新手們不再懼怕那些看起來復雜的正則表達式。
定義
正則表達式是用于匹配字符串中字符組合的模式。
分析模式
首先我會把常用的正則表達式列出來,接著介紹與該表達式有關的知識點,然后結合知識點并借用例子來逐步分析該正則表達式。最后我會列出一些匹配該正則表達式的例子。為了保證閱讀質量,知識點分析不會一直重復,到后來會只介紹重要或新出現的知識點。
相關連接
正式開始
用戶名:
^[a-z0-9_-]{3,16}$
- 知識點:
-
^
:匹配輸入的開始 -
$
:匹配輸入的結束 -
{n,m}
:n和m都是正整數。匹配前面的字符至少n次,最多m次。如果n或者m的值為0,這個值被忽略。 -
[a-z]
:表示范圍a-z的一個字符集合,可以與該區間內任何字符匹配。
-
- 從左到右逐步分析:
- 字符
^
開始匹配輸入 -
[a-zA-Z0-9_-]
表示該字符可以與a-z,A-Z,0-9,下劃線,破折號范圍內的任意一個字符匹配 -
{3,16}
表示用戶名里有最少3個或者最多16個上述字符
- 字符
- 匹配舉例:
abcde
- 不匹配舉例:
-
ab
(太短)
-
手機號:
/^1[3|4|5|7|8][0-9]{9}$/
//手機號的正則表達式通常被分為移動,聯通等幾種,因為本文注重于對正則表達式的理解,所以不作分類,只對一個通用的表達式進行分析。手機號的范圍以百度百科為準,即第一位是1開頭,第二位有3,4,5,7,8,第三位及之后的數字都是0-9的范圍
- 知識點:
-
^
:匹配輸入的開始 -
$
:匹配輸入的結束 -
[a|b]
:表示匹配a或者b的一個字符集合 -
[0-9]
:表示范圍0-9的一個字符集合,可以與該區間內任何字符匹配 -
{n}
:n是一個正整數,匹配了前面一個字符剛好發生了n次
-
- 從左到右逐步分析:
- 字符
^
開始匹配輸入 - 1規定了必須第一位是1
-
[3|4|5|7|8]
表示第二位可以從3,4,5,7,8這幾個數字中任選其一 -
[0-9]{9}
表示匹配任意9個在0-9范圍內的數字,同時也規定了必須有9個數
- 字符
- 匹配舉例:
13456789456
- 不匹配舉例
-
12345678987
(第二位不屬于3,4,5,7,8任何一個) -
138078787
(第三位開始少于9個數)
-
郵箱:
/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
-
知識點:
-
^
:匹配輸入的開始 -
$
:匹配輸入的結束 -
+
:匹配前面一個表達式一次或多次 -
\
:將其后的特殊字符,轉義為字面量 -
[a-z]
:表示范圍a-z的一個字符集合,可以與該區間內任何字符匹配。
-
-
從左到右逐步分析(舉例:wys123@gmail.com):
- 字符
^
開始匹配輸入 -
[a-zA-Z0-9_-]
表示該字符可以與a-z,A-Z,0-9,下劃線,破折號范圍內的任意一個字符匹配,緊跟著的加號表示可以匹配多個上述范圍內的字符 (第2步代表wys123這個部分) -
@
規定了必須有@
符號 - [a-zA-Z0-9_-]+ 的含義與第2步介紹的一樣 (代表gmail部分)
-
\.
將.
從特殊字符轉義為字面量,規定了必須有.
(gmail.com中的".") -
[a-zA-Z0-9_-]+
的含義還是與第2步中的一樣 (代表com部分)
- 字符
-
匹配舉例:
mario_a@qq.com
mario-a@qq.com
mario@a-b.c
-
不匹配舉例:
marioqq.com
mario@qqcom
mar&io@qq.com
十六進制數字:
^#?([a-f0-9]{6}|[a-f0-9]{3})$
-
知識點:
-
?
:匹配前面一個表達式0次或者1次 -
(x)
: 匹配x并記住該匹配項,括號被稱為捕獲括號 -
{n}
:n是一個正整數,匹配了前面一個字符剛好發生了n次 -
a|b
:匹配a或者b
-
-
從左到右逐步分析:
- 因為
?
的存在,所以#
在這里是可有可無的 -
([a-f0-9]{6}|[a-f0-9]{3})
可以看成(A|B)
,其中A=[a-f0-9]{6}
,代表一個剛好存在6個范圍在af或者09的字符的集合;B=[a-f0-9]{3}
,代表一個剛好存在3個范圍在a-f或者0-9的集合。那么通俗講,不考慮#
,被匹配的字符集合長度為3或者6,這3個或者6個字符必須都在范圍a-f或0-9內。
- 因為
-
匹配舉例:
#000000
#ffffff
aff
-
不匹配舉例:
-
#00
(位數錯誤,#后面不是3位或者6位) -
#-aaaaa
(范圍錯誤,-不在范圍a-f或0-9內)
-
國內電話號碼(帶區號):
^\d{3}-\d{8}|\d{4}-\d{7,8}$
-
知識點:
-
\d
:匹配一個數字 -
{n}
:n是一個正整數,匹配了前面一個字符剛好發生了n次 -
{n,m}
:n和m都是正整數。匹配前面的字符至少n次,最多m次。如果n活著m的值為0,這個值被忽略。
-
分析:
這個例子與16進制的例子很像,可以將其看成X|Y
,其中X=\d{3}-\d{8}
,代表3個數字+一個-
+8個數字;Y=\d{4}-\d{7,8}
,代表4個數字+一個-
+7或8個數字,所以該正則表達式匹配了兩種電話號碼的模式-
匹配舉例:
021-86279200
0574- 86279200
-
不匹配舉例:
-
021-86278
(不匹配,位數不夠) -
86279200
(不匹配,雖然有時候也會需要匹配這一類不帶區號的電話號碼,但在本文采用的表達式中強制要求加上區號,所以這個例子也不匹配)
-
URL:
^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$
-
知識點:
-
\
:可以將其后的特殊字符,轉義為字面量 -
?
: 匹配前一個表達式0次或1次 -
\w
:匹配一個單字字符(字母、數字或下劃線),等價于[A-Za-z0-9_] -
*
: 匹配前一個表達式0次或多次
-
-
分析:
這個表達式相對前面幾個較復雜,我們先把它分塊來看:
URL分析- 紅色部分:s后面的問號代表可以匹配http或者https,
\
將/
轉義成了字面量,所以連在一起就是http://
或者https://
,然后紅色部分最后的問號代表https://
或者http://
可以都不要,直接寫url比如www.baidu.com
,這也是可以被匹配的 - 橙色部分:
[\da-z\.-]
代表了表示了范圍為數字,小寫字母,點或者-
的字符集合,后面的加號表示可以匹配多個上述范圍內的字符 - 綠色部分:
\
將.
轉義成了字面量,這里表示必須要有一個.
字符 - 藍色部分:
[a-z\.]
代表了范圍為小寫字母或者點的字符集合,{2,6}表示可以有最少2個,最多6個上述范圍的字符。橙色,綠色和藍色部分組成了域名 - 粉色部分:
[\/\w\.-]
表示了范圍為/
,字母,數字,下劃線,點或者"-"的字符集合,后面的*
表示可以匹配這個式子0次或者多次。其實筆者在這里對為什么要有兩個*
字符表示疑問,我認為這兩個*
號作用相同,取其一即可,粉色部分是路徑和文件名 - 綠色部分:
\
將/
轉義成字面量后,跟隨的?
代表該/
可有可無,換句話說,www.baidu.com/something
和www.baidu.com/something/
都可以被匹配。
- 紅色部分:s后面的問號代表可以匹配http或者https,
-
匹配舉例:
https://www.google.co.uk/
https://zh.wikipedia.org/wiki/
-
不匹配舉例:
-
htts://www.baidu.com
(http要么不存在要么整個存在,這里不符合)
-
匹配首尾空白字符(可用來刪除首尾空白字符)
^\s*|\s*$
- 知識點:
-
\s
:匹配一個空白字符,包括空格、制表符、換頁符合換行符 -
^
:匹配輸入的開始 -
$
:匹配輸入的結束 - 注意
\s
和\S
是相反的意思,不要混淆大小寫
-
- 分析:
- 其實往往越簡單的表達式越難理解,對于這個表達式我決定用多個例子一步步解析。首先我們先詳細測試一下表達式
^a|b$
所能匹配的字符串,在字符串"aabb"中,其匹配了第一個a和第二個b,換句話說,匹配了首尾的a和b。再來看一看^a*|b*
,這次用字符串aacaabbcbb
來測試,可以看到匹配了首尾的"aa"和"bb",但并沒有匹配兩個c之間的aabb
,這是因為^
和$
存在的緣故,實際上它會匹配開始后和結束前的a*
或b*
字符串. 那么現在我們重新來看上述的表達式,將a
和b
都替換為\s*
后,該式子匹配的是字符串首尾的連續空白字符,這里有兩個關鍵詞:首尾和連續。之所以只匹配首尾,是因為字符串句首是匹配開始后最先被判斷的,而字符串句尾是匹配結束前最后被判斷的,所以符合了符號^
和$
所代表的意思,至于連續,則是由*
所決定的,如果不帶*
,那么只會匹配首尾的一個空白字符。
- 其實往往越簡單的表達式越難理解,對于這個表達式我決定用多個例子一步步解析。首先我們先詳細測試一下表達式
- 匹配舉例:
-
aacaab bcbb
(只匹配首尾空格,中間的空格不匹配)
-
- 不匹配舉例:
-
a b c
(只匹配首尾空格,中間的空格不匹配)
-
未涉及到的常用特殊字符(摘自MDN)
-
(?:x)
:匹配 'x' 但是不記住匹配項。這種叫作非捕獲括號,使得你能夠定義為與正則表達式運算符一起使用的子表達式。來看示例表達式 /(?:foo){1,2}/。如果表達式是 /foo{1,2}/,{1,2}將只對 ‘foo’ 的最后一個字符 ’o‘ 生效。如果使用非捕獲括號,則{1,2}會匹配整個 ‘foo’ 單詞。 -
x(?=y)
:匹配'x'僅僅當'x'后面跟著'y'.這種叫做正向肯定查找。
例如,/Jack(?=Sprat)/會匹配到'Jack'僅僅當它后面跟著'Sprat'。/Jack(?=Sprat|Frost)/匹配‘Jack’僅僅當它后面跟著'Sprat'或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配結果的一部分。 -
[^xyz]
:一個反向字符集。也就是說, 它匹配任何沒有包含在方括號中的字符。你可以使用破折號(-)來指定一個字符范圍。任何普通字符在這里都是起作用的。例如,[^abc] 和 [^a-c] 是一樣的。他們匹配"brisket"中得‘r’,也匹配“chop”中的‘h’。
總結
至此,借用一些常見的正則表達式,已經把MDN里提到的大部分特殊字符都分析了一遍,筆者本人也是一個新手,雖然花了很多功夫試圖透徹理解上述各個表達式的含義,但文章中肯定還存在理解有偏差的地方,歡迎指正。