工具-能寫會看正則表達式

語言: CN / TW / HK

theme: nico

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第25天,點擊查看活動詳情

正則表達式,很常用的一個技能點,但是一般的開發流程中都是這樣的: 1. 需要驗證數據 2. 上網搜一下正則表達式 3. CV 搞定!!!

今天有時間回看了一下文檔,簡單整理了一下里面需要注意的點,並且通過分析幾個常見的正則表達式,下次遇到正則爭取不再只依靠 CV 大法!

基礎部分

基本語法

/正則表達式主體/修飾符(可選)

解釋一下就是,正則表達式是由 兩個 / 包裹的 正則表達式主體 和緊跟在後面的修飾符組成的。所以,也只有兩個斜線是固定格式,裏面的所有內容都屬於 正則表達式主體。

為什麼這囉嗦呢?

因為我曾經有一段時間以為正則表達式的語法是 /^正則表達式主體$/修飾符,即 以 /^ 開始,$/ 結束。其實並不是,是因為 ^ 的含義是 以 ^後面的內容開始,而 $的含義是 以 $之前的結束,它倆有個好聽的名字,叫量詞。下面會單獨記錄一下。

修飾符

修飾符只有三個,而且都很實用:

  • i : 忽略大小寫
  • g : 匹配所有滿足正則表達式的
  • m : 執行多行匹配

但是m用的比較少,前面兩個是很常見的!

方括號

| 表達式 | 描述 | | -- | -- | | [abc] | 查找方括號之間的任何字符。 | | [^abc] | 查找任何不在方括號之間的字符。 | | [0-9] | 查找任何從 0 至 9 的數字。 | | [a-z] | 查找任何從小寫 a 到小寫 z 的字符。 | | [A-Z] | 查找任何從大寫 A 到大寫 Z 的字符。 | | [A-z] | 查找任何從大寫 A 到小寫 z 的字符。 | | [adgk] | 查找給定集合內的任何字符。 | | [^adgk] | 查找給定集合外的任何字符。 | | (red\|blue\|green) | 查找任何指定的選項。|

方括號是實現正則表達式能夠靈活匹配任何場景的核心元素,他代表的就是一個集合,可以滿足模糊匹配的效果。

元字符

| 元字符 | 描述 | | -- | -- | | . | 查找單個字符,除了換行和行結束符。 | | \w | 查找數字、字母及下劃線。 | | \W | 查找非單詞字符。 | | \d | 查找數字。 | | \D | 查找非數字字符。 | | \s | 查找空白字符。 | | \S | 查找非空白字符。 | | \b | 匹配單詞邊界。 | | \B | 匹配非單詞邊界。 | | \0 | 查找 NULL 字符。 | | \n | 查找換行符。 | | \f | 查找換頁符。 | | \r | 查找回車符。 | | \t | 查找製表符。 | | \v | 查找垂直製表符。 | | \xxx | 查找以八進制數 xxx 規定的字符。 | | \xdd | 查找以十六進制數 dd 規定的字符。 | | \uxxxx | 查找以十六進制數 xxxx 規定的 Unicode 字符。 |

元字符是對一些常用字符進行了歸類合併,可以代指某一類的匹配目標值。配合方括號和語法來實現靈活匹配的效果。

量詞

| 量詞 | 描述 | | -- | -- | | n+ | 匹配任何包含至少一個 n 的字符串。例如,/a+/ 匹配 "candy" 中的 "a","caaaaaaandy" 中所有的 "a"。 | | n | 匹配任何包含零個或多個 n 的字符串。例如,/bo/ 匹配 "A ghost booooed" 中的 "boooo","A bird warbled" 中的 "b",但是不匹配 "A goat grunted"。 | | n? | 匹配任何包含零個或一個 n 的字符串。例如,/e?le?/ 匹配 "angel" 中的 "el","angle" 中的 "le"。| | n{X} | 匹配包含 X 個 n 的序列的字符串。例如,/a{2}/ 不匹配 "candy," 中的 "a",但是匹配 "caandy," 中的兩個 "a",且匹配 "caaandy." 中的前兩個 "a"。 | | n{X,}| X 是一個正整數。前面的模式 n 連續出現至少 X 次時匹配。例如,/a{2,}/ 不匹配 "candy" 中的 "a",但是匹配 "caandy" 和 "caaaaaaandy." 中所有的 "a"。 | | n{X,Y} | X 和 Y 為正整數。前面的模式 n 連續出現至少 X 次,至多 Y 次時匹配。例如,/a{1,3}/ 不匹配 "cndy",匹配 "candy," 中的 "a","caandy," 中的兩個 "a",匹配 "caaaaaaandy" 中的前面三個 "a"。注意,當匹配 "caaaaaaandy" 時,即使原始字符串擁有更多的 "a",匹配項也是 "aaa"。 | | n$ | 匹配任何結尾為 n 的字符串。 | | ^n | 匹配任何開頭為 n 的字符串。 | | ?=n | 匹配任何其後緊接指定字符串 n 的字符串。 | | ?!n | 匹配任何其後沒有緊接指定字符串 n 的字符串。 |

量詞也是標識,如上面表格中所示,n 是指由上面的 方括號和元字符組成的,n 之外的內容是量詞。

如開篇所説,我一開始以為正則表達式的語法是 /^$/ 結束,看到這裏以後才確定, ^ 和 $ 是量詞,各有其含義,並不是語法中必要的。

靈活使用量詞能夠對於要求比較高的正則精準匹配。

常見表達式分析

這個文章的目的不是為了蒐集各種各樣的正則表達式而寫的,而是為了能夠讀懂正則表達式,從而方便對現有的正則進行改造或者判斷正則是不是有問題。

所以只會拿幾個比較有特色的正則表達式進行簡單的分析,因為這些正則是從其他地方借鑑過來的,可能有問題,但是不妨礙我們學習,如果真的看出問題來了,才説明真的有了進步了!

用户名驗證

這個正則驗證的是用户名,要求只能出現 數字大小寫字母-_ 這幾樣元素,長度在 4 到 16 位之間。

是一個很常見、很實用的場景,不只是為了驗證用户名,很多複雜的驗證都是這個模板,即從第一位開始到最後一位,每一位都要滿足一定的條件,而且長度有限制。

js /** * /^ 開始 * [] 查找方括號中間的任何字符 * a-z 小寫字母 * A-Z 大寫字母 * 0-9 數字 0-9 * _ 下劃線 * {4,16} 長度 * $/ 結束 */ const userPattern = /^[a-zA-Z0-9_-]{4,16}$/; const userStr1 = "Admin"; console.log("userStr1.test.userPattern :", userPattern.test(userStr1));

如上所示,我們可以通過修改方括號裏面的內容,就可以使正則表達式支持其他字符,通過修改長度就可以滿足其他長度的校驗。

Email 和 手機號 驗證

```js /* * Email 正則表達式 * 分為三部分 * 第一部分:對每個字符進行校驗,只能是字母+數字和_-.的組合 * 第二部分:以 @ 開頭且其他部分和第一部分一致 * 第三部分:以 . 開頭然後由大小寫字母組成長度為 2-4 的字符串結尾 / const ePattern = /^[A-Za-z0-9_-.]+\@[A-Za-z0-9_-.]+.[A-Za-z]{2,4}$/; const email = "[email protected]"; console.log("email.test.ePattern :", ePattern.test(email));

// 手機號正則 var mPattern = /^[1][3458][0-9]{9}$/; ```

這個正則表達式就是在用户名的那個基礎上進行了擴展,可以使正則表達式支持對一個固定模式的文本進行校驗。就像這次個正則表達式就是對 [email protected] 模式的字符串進行一個校驗。

需要格外關注的是,這裏面的 + 和 {2,4} 都是量詞,如果沒有很細的去了解的話容易把 + 看做加號或者連字符,其實它的含義是 匹配任何包含至少一個 n 的字符串

而手機號的則需要關注的是 [3458] 代指的是第二位只能是 3/4/5/8 ,而且必須跟在 1 後面,然後後面跟9位數字。

身份證號和url驗證

難度上升,這兩個正則要比上面的複雜一點。

js // 身份證號(18位)正則 /* * 第一部分,前六位地區:[1-9]\d{5},第一位是 1-9中的一個,後面5位是數字。\d 元字符,代指數字 * 第二部分,中間八位生日:(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31) * 年:(18|19|([23]\d))\d{2} 這裏貌似不對,應該是 (18|19|20)\d{2} 即 從1800-2099 年 * 月:((0[1-9])|(10|11|12)) 01-09,然後是 10/11/12 * 日:(([0-2][1-9])|10|20|30|31) 這個就包含了除 00 之外的 0-31 的數字了 * 第三部分,四位數字或者三維數字加X:\d{3}[0-9Xx] * 除了這三部分之外:^標識開始 $標識結束 */ var cP = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; 18位的身份證號大家都熟悉吧!前6位是家鄉的代碼,中間8位是出生日期,後面4位則是四位數字或者三維數字加字母X。

知道了這個規則,回過頭看上面的正則表達式。不難發現 的部分是有問題的,這個正則表達式無疑是把年份校驗在了 1800 - 3999 年之間,不太合理。

js // url /* * 第一部分:((https?|ftp|file)://)? 問號代表 0個或1個,最多一個。涉及到的為 https 中的s 和 整個第一部分 * 第二部分:([\da-z.-]+).([a-z.]{2,6}) 包含.組成的 xxx.xxx.xxx 結構的字符串,xxx作為一個塊,第一塊可以包含數字,後面的塊只有小寫字母,最多有 7 個塊 * 第三部分:([/\w .-]*)*/? 路由地址,由 \w、-、.組成,* 和 ?都是量詞 xxx/xxx/ 或 xxx/xxx 結構 */ var urlP= /^((https?|ftp|file)://)?([\da-z.-]+).([a-z.]{2,6})([/\w .-]*)*/?$/;

url 也是很常用的一個數據類型了,而且數據很靈活,除了前半部分之外其他部分沒法確定。而這個正則表達式正是這個思路,首先是開頭以 http|https|ftp|file 加緊跟着的 :// 組成的第一部分,然後是域名由至少為 xxx.xxx.xxx 組成的 域名作為第二部分,緊接着第三部分是域名層級。

但是這個正則表達是第三部分,感覺怪怪的,/? 的位置感覺有點不對勁。

懸念

下面這個留給有緣人分析一下,算是作業吧:

// 密碼強度正則,最少6位,包括至少1個大寫字母,1個小寫字母,1個數字,1個特殊字符 var pPattern = /^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@``#$%^&*? ]).*$/; console.log("=="+pPattern.test("iFat3#"));

配套方法

正則表達式只是一種語法規範,如上面我借鑑別人的正則表達式來分析一樣,是可以拿來直接用的,但是如何用就涉及到一系列的 API 了。

API 主要分為兩類,一類是 String 對象上的幾個方法:replace/split/match/search,還有 RegExp 對象上的幾個方法:test/exec。

下面逐一測試並説明:

String.replace

這個方法大家幾乎每個項目都在用,作用是替換字符串中的內容。

語法

stringObject.replace(regexp/substr,replacement)

一些小技巧

```js // 替換所有,對 g 修飾符的使用 var str="Welcome to Microsoft! " str=str + "We are proud to announce that Microsoft has " str=str + "one of the largest Web Developers sites in the world."

document.write(str.replace(/Microsoft/g, "W3School"))

// 首字母轉為大寫 name = 'aaa bbb ccc'; uw=name.replace(/\b\w+\b/g, function(word){ return word.substring(0,1).toUpperCase()+word.substring(1);} );

// replacement 中的 $ 字符具有特定的含義 // $1、$2、...、$99 表示從左到右,正則子表達式(組)匹配到的文本

// 將把 "Doe, John" 轉換為 "John Doe" 的形式 var name = "Doe, John"; name.replace(/(\w+)\s, \s(\w+)/, "$2 $1"); // John Doe

// 把所有的花引號替換為直引號 var name2 = '"a", "b"'; name2.replace(/"([^"]*)"/g, "'$1'"); // "'a', 'b'"

// $& 表示 regexp 相匹配的子串。 var str = 'Visit Microsoft'; str = str.replace(/Visit Microsoft/g,"$& ,W3School"); console.log(str); // Visit Microsoft ,W3School

// $位於匹配子串左側的文本。 var str = 'Visit Microsoft'; str = str.replace(/Microsoft/g,'$'); console.log(str); // Visit Visit

// $' 位於匹配子串右側的文本。 var str = 'Visit Microsoft'; str = str.replace(/Visit/g,"$'"); console.log(str); // Microsoft Microsoft

// $$ 直接量符號 插入一個"$" var str = "javascript"; str = str.replace(/java/,"$$") console.log(str); // $script ```

String.split

切割字符串,將字符串轉換為數組最常用的的方法

語法

stringObject.split(separator,howmany)

js const str = "hello world!"; const reg1 = /l/g; console.log("String.split.reg :", str.split(reg));

String.match

match() 方法可在字符串內檢索指定的值,或找到一個或多個正則表達式的匹配。

語法

stringObject.match(searchvalue) stringObject.match(regexp)

js const str = "hello world!"; const reg = /l/; console.log("String.match.reg", str.match(reg));

String.search

search() 方法也接受字符串作為搜索參數。字符串參數將被轉換為正則表達式:

語法

stringObject.search(regexp)

返回值

stringObject 中第一個與 regexp 相匹配的子串的起始位置。

註釋:如果沒有找到任何匹配的子串,則返回 -1。 var str = "Visit W3School!"; var n = str.search("W3School");

RegExp.test

test() 是一個正則表達式方法。它通過模式來搜索字符串,然後根據結果返回 true 或 false。

語法

RegExpObject.test(string)

js const str = "hello world!"; const reg = /l/; console.log("RegExp.test.reg:", reg.test(str));

RegExp.exec

exec() 方法用於檢索字符串中的正則表達式的匹配。

語法

RegExpObject.exec(string)

返回值

返回一個數組,其中存放匹配的結果。如果未找到匹配,則返回值為 null。

js const str = "hello world!"; const reg = /l/; console.log("RegExp.exec.str:", reg.exec(str));

總結

其實除了一些特殊的項目,很少會有遇到經常需要去寫正則表達式的地方,而整理這篇文檔的目的也不是為了鼓勵大家去手寫正則表達式,正如我的標題,能寫會看就行了,至少要會看。

正常情況下,遇到需要使用正則表達是的場景,正常的開發都會去上網搜一下現成的,而且90%以上的場景都能搜到,但是這個正則是否滿足你的使用場景?是否你們的場景有特殊要求?這就需要開發人員能夠基於借鑑過來的正則表達式進行二次開發,或者照着改一個出來了!

另一方面,一些簡單的場景,例如 replace 的場景,一般來説都可以順手寫一個出來的,這些場景再去頻繁的百度,就有點過分了。

希望對大家有用,希望這次總結能在腦子裏面留痕。

參考文檔