工具-能寫會看正則表達式
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 的場景,一般來説都可以順手寫一個出來的,這些場景再去頻繁的百度,就有點過分了。
希望對大家有用,希望這次總結能在腦子裏面留痕。