常用正則表達式分析(入門)

寫在開頭

在編碼過程中,正則表達式一直是經常會出現但又困擾我們的知識點,筆者就是每次遇到正則表達式就相當頭痛,往往都是從網上復制一個表達式到代碼里草草了事。但其實如果不真正理解正則表達式的含義,我們就無法對其進行修改來應對特殊的需求。所以本文選取幾個常用的正則表達式進行分析,旨在覆蓋基礎的正則表達式知識,讓和我一樣的新手們不再懼怕那些看起來復雜的正則表達式。

定義

正則表達式是用于匹配字符串中字符組合的模式。

分析模式

首先我會把常用的正則表達式列出來,接著介紹與該表達式有關的知識點,然后結合知識點并借用例子來逐步分析該正則表達式。最后我會列出一些匹配該正則表達式的例子。為了保證閱讀質量,知識點分析不會一直重復,到后來會只介紹重要或新出現的知識點。

相關連接

正式開始

用戶名:

    ^[a-z0-9_-]{3,16}$
  • 知識點:
    • ^ :匹配輸入的開始
    • $ :匹配輸入的結束
    • {n,m} :n和m都是正整數。匹配前面的字符至少n次,最多m次。如果n或者m的值為0,這個值被忽略。
    • [a-z] :表示范圍a-z的一個字符集合,可以與該區間內任何字符匹配。
  • 從左到右逐步分析:
    1. 字符 ^ 開始匹配輸入
    2. [a-zA-Z0-9_-]表示該字符可以與a-z,A-Z,0-9,下劃線,破折號范圍內的任意一個字符匹配
    3. {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):

    1. 字符^開始匹配輸入
    2. [a-zA-Z0-9_-]表示該字符可以與a-z,A-Z,0-9,下劃線,破折號范圍內的任意一個字符匹配,緊跟著的加號表示可以匹配多個上述范圍內的字符 (第2步代表wys123這個部分)
    3. @規定了必須有@符號
    4. [a-zA-Z0-9_-]+ 的含義與第2步介紹的一樣 (代表gmail部分)
    5. \..從特殊字符轉義為字面量,規定了必須有. (gmail.com中的".")
    6. [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
  • 從左到右逐步分析:

    1. 因為?的存在,所以#在這里是可有可無的
    2. ([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/somethingwww.baidu.com/something/ 都可以被匹配。
  • 匹配舉例:

    • 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*字符串. 那么現在我們重新來看上述的表達式,將ab都替換為\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里提到的大部分特殊字符都分析了一遍,筆者本人也是一個新手,雖然花了很多功夫試圖透徹理解上述各個表達式的含義,但文章中肯定還存在理解有偏差的地方,歡迎指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容