厲害!這篇正則表達式竟寫的如此詳盡

厲害!這篇正則表達式竟寫的如此詳盡

https://www.zoo.team/article/regexp

前言

爲什麼要學正則表達式?

作爲一個菜鳥程序員,遇到複雜的正則時往往會求助搜索引擎。雖然能夠解決燃眉之急,但往往會有 [邊界值] https://blog.csdn.net/weixin_33994444/article/details/93408973 和團隊正則不統一的問題。而這種問題往往會被細測測出來,造成不必要的麻煩。對於這種情況,最好的解決方式就是去喫透它,最終能將它手寫出來。所謂知其然,也要知其所以然。

什麼是正則表達式?

1951 年, 一位名叫史蒂芬 · 克林(Stephen Kleene)的數學科學家,發表了一篇名叫《神經網事件的表示法》的論文。論文中引入了正則表達式的概念。直至七十多年後的今天,正則表達式仍然影響着我們互聯網生活的方方面面。

比如需要填入符合特定規則的密碼、手機號、郵箱等。

又比如我們需要對輸入框加一箇中文、英文或價格等輸入限制

不過在我們學習正則表達式之前,我們需要先熟悉下它的調試工具

怎麼檢測我的正則對不對呢?

編輯器內置的搜索工具

我們可以使用 Vscode (https://code.visualstudio.com/) 提供的正則的搜索方法,來使用正則匹配我們需要搜索的內容。

函數調用

也可以在自己熟悉的編程語言中調用函數去測試。

在線測試網站

亦或是使用我這邊推薦的一個 [在線測試網站] https://c.runoob.com/front-end/854/

瞭解了調試工具後我們開始從基礎學習吧。

基礎

限定符 (Quantifiers)

?

“?” 表示前面的字符可以出現 1 次或者 0 次。說簡單點就是 "co" 中的 "o" 這個字母可有可無。

+

“+” 表示前面的字符至少匹配 1 次或多次。比如上列中 ”poverty“、“poor” 得到了匹配。“premier” 卻沒有匹配。

*

“*” 可以匹配 0 個或者多個字符。可以看到出現 0 次的 “pr” 和 2 次的 “poor” 得到了匹配。我們如何匹配固定次數一定範圍的字符呢?

{...}

“{...}” 可以用來匹配固定數量的字符或某個範圍內的字符。

如上圖所示:

(...)

以上都講的是匹配單個字符,當我們想匹配多個字符該怎麼辦呢?

我們可以將我們需要匹配的字符用 “(...)” 括起來,可以看到 “(...)” 搭配我們上文所學的 “+“ 成功匹配了 ”banana“ 和 ”anna“。

或運算符 (OR Operator)

當我們需要匹配兩個或多個條件時我們就需要使用 "|" (或運算符)。圖中我們匹配了擁有 “an” 或 “na” 的單詞。值得注意的是這裏的 “|” 最外層的 "()" 是必不可少的。

字符類 (Character class)

比如我們要匹配擁有 “123” 中任意字符的字符串則需要使用 “[...]”, 需要匹配的字符只能取自方括號中的內容。另外我們也可以使用字符類去匹配指定範圍,如 [a-z]、[0-9]、[\u4e00-\u9fa5] 等。

另外我們也可以在前面加 ”^“ 用來匹配非此範圍的字符串,如上我們就匹配了非英文的字符串。

另外正則表達式也提供了很多元字符可以讓我們簡寫我們的正則表達式。

元字符 (Meta character)

我們可以使用 “\d” 代替我們之前的 “[0-9]”, 這裏還有兩個特殊字符 “^” 會匹配開始 (這裏要注意與我們前面講到的字符類中的運用要區分),“$“ 會匹配結束。

  • \d,\w,\s - 匹配數字、字符、空格 (分別代表着:digit、word、space)。

  • \D,\W,\S - 匹配非數字、非字符、非空格。

  • . - 除換行符以外的所有字符 (句號句子的結束符)。

  • ^ - 字符串開頭。

  • $ - 字符串結尾。

接下來我們來學習一下正則表達式的高級概念

高級概念

懶惰匹配和貪婪匹配 (Lazy and Greed)

當我們想要匹配文本中的 HTML 時我們會寫下如下正則表達式。

根據前面的知識我們知道,此正則將會匹配 <開頭> 結尾中間可以有至少 1 個任意字符。但結果是匹配了 “<” 和“>” 標籤包含的所有內容。

我們可以在正則表達式中加 “?” 來開啓懶惰匹配。開啓懶惰匹配後,正則會儘可能少的匹配。所以當匹配的時候發現標籤  “ ” 已經是符合要求的,所以會匹配 標籤,然後繼續向下匹配,發現 標籤也是符合要求的,繼續向下匹配,發現文字不符合要求,繼續向下,發現 標籤都是符合要求的,所以最後會匹配 , , , 四個標籤。

分組 (Group)

前面我們認識了 “(...)” 的用法,將其結合我們後面所學的知識就是正則的分組

如上圖所示第一個分組中我們匹配 4 個數字,第二個分組中我們匹配 7 個數字。中間使用 “-” 進行連接,便很容易匹配到了文本中出現的座機電話號。

非捕獲分組 (Non-capture Group)

非捕獲分組:(?:表達式),分組匹配之後,不需要的用 “?:” 語法過濾子表達式內容。也就是代碼匹配,但是不保存。

在使用非捕獲前:

在使用非捕獲後:

通過 .exec 方法並沒有捕獲到月份。

回溯 (Flash Back)

當我們想匹配一個正確的 HTML 標籤時,使用 "<[\w]+>.*</[\w]+>"。

可以看到雖然可以匹配 HTML 開始和結束標籤,但是卻不能校驗前後的一致性。如 “” 並不是 “” 的結束標籤。

我們可以把後面的部分改成 “</\1>” 其中 “\1” 就是引用第一個分組。這樣一來我們就可以匹配正確的 HTML 標籤了。

斷言 (Assertion)

斷言有些地方也叫環視 (Lookaround),它只進行子表達式的匹配,不佔有字符,匹配到的內容不保存到最終的匹配結果。

正向先行斷言

正向先行斷言:(?=表達式),指在某個位置往右看,所在的位置右側必須匹配表達式

我們可以看到 “/ 喜歡(?= 你)” 正確匹配到了 “你” 前面有 “喜歡” 的文本。

實現一個密碼強度校驗

至少有一個大寫字母。至少有一個小寫字母。至少有一個數字。至少有 8 個字符。

反向先行斷言

反向先行斷言:(?!表達式),指在某個位置往右看,不能存在表達式中的內容。

如上就排除了 “喜歡” 後面有 “你” 的字符串。

正向後行斷言

正向後行斷言:(?<=表達式),指在某個位置往左看,存在表達式中的內容。

如上就匹配了 “喜歡” 前面有 “我” 的字符串。

反向後行斷言

反向後行斷言:(?<!表達式),指在某個位置往左看,不能存在表達式中的內容。

如上就排除了 “喜歡” 前面有 “我” 的字符串。

至此正則表達式的高級部分學完啦,接下來我們學習正則提供的方法

方法

在 JavaScript 中,RegExp 對象是一個預定義了屬性和方法的正則表達式對象。

test()

該方法用於檢測一個字符串是否匹配某個正則表達式,匹配返回 true,不匹配返回 false。

exec()

該方法用於檢測字符串中對正則表達式的匹配。

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

除了正則自身攜帶的方法,配合 String 對象的方法一起使用也會有額外的效果。

配合 String 提供的方法

match

match 這個方法主要用來提取數據,它配合分組的()一起使用,可以很方便的提取數據。

var str = '2022-04-22'
var reg = /^(\d{4})-(\d{2})-(\d{2})$/
console.log(str.match(reg));
//  ['2022-04-22''2022''04''22', index: 0, input: '2022-04-22', groups: undefined]

replace

replace 這個 api 主要用於替換數據,多用於字符串的處理和轉義。

var str = '賈維斯:您今天共產生了8個BUG'
var reg = /\w{3}/g
console.log(str.replace(reg,"Beautiful Code"))
// 賈維斯:您今天共產生了8個Beautiful Code

什麼是 $ 1 $ 2

let str = "前端1組-開發部";
console.log(str.replace(/(.{4})-(.{3})/, "$2 $1"));
// 開發部 前端 1 組

$1,$2 上就是按順序對應小括號裏面的分組 捕獲到的內容。這裏我們將 2 組和 1 組進行內容替換,就得到了替換後的內容。

split

split 主要用於來切分字符串爲數組,它的第一個參數也可以爲正則的形式。

const str1 = '2022-04-21'
const str2 = '2022.04.22'
const str3 = '2022/04/23'
const regsSplit = /[\.\-\/]/
console.log(str1.split(regsSplit))
console.log(str2.split(regsSplit))
console.log(str3.split(regsSplit))
// ['2022''04''21']
// ['2022''04''22']
// ['2022''04''23']

瞭解完了,結合 String 用法,我們再來了解一下兼容性問題

正則表達式兼容性調研

在我們日常使用中,一定會遇到兼容性問題。這裏主要對一些不完全兼容的方法進行調研。

@@split

@@split  方法切割 String 對象爲一個其子字符串的數組 。

var re = /-/g;
var str = '2022-01-02';
var result = re[Symbol.split](str);
console.log(result);  // ["2022""01""02"]

兼容性

@@match

對正則表達式匹配字符串時,@@match  方法用於獲取匹配結果。

var re = /[0-9]+/g;
var str = '2022-01-02';
var result = re[Symbol.match](str);
console.log(result);  // ["2022""01""02"]

兼容性

@@search 方法執行了一個在給定字符串中的一個搜索以取得匹配正則模式的項。

var re = /-/g;
var str = '2016-01-02';
var result = re[Symbol.search](str);
console.log(result);  // 4

兼容性

@@replace

@@replace 方法會在一個字符串中用給定的替換器,替換所有符合正則模式的匹配項,並返回替換後的新字符串結果。用來替換的參數可以是一個字符串或是一個針對每次匹配的回調函數。

var re = /-/g; 
var str = '2016-01-01';
var newstr = re[Symbol.replace](str, '.');
console.log(newstr);  // 2016.01.01

兼容性

flags

flags 屬性返回一個字符串,由當前正則表達式對象的標誌組成。

/foo/ig.flags;   // "gi"
/bar/myu.flags;  // "muy"

兼容性

dotAll

正則中的點匹配就是  dotAll ,都是匹配任意字符,但是很多字符是無法匹配的。例如:

console.log(/foo.bar/.test('foo\nbar'))
// false
console.log(/foo.bar/.test('fooabar'))
// true

加上  s  可以匹配換行符

console.log(/foo.bar/s.test('foo\nbar'))
// true

兼容性

最後我們讓來點乾貨

乾貨

保留兩位小數的價格輸入框

具體代碼如下:

// 輸入限制
const changePiece = (e) =>{
      e.target.value = e.target.value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1');
  }
  return (
    <div>
       <input type="text" onKeyUp={ (e) ={changePiece(e)}} /> 
    </div>
  );

常用正則表達式

// 手機號碼的校驗
const phoneReg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/
// 身份證的校驗
const idCardReg = /^[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]$/
// URL的校驗
const urlReg = /^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
// 郵箱的校驗
const emailReg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/
// 日期 YYYY-MM-DD 校驗
const dateReg = /^\d{4}(\-)\d{1,2}\1\d{1,2}$/

以上乾貨大家也可以通過上文所學的知識自己嘗試實現,自己實踐纔會有更深刻的印象和更深度的認知。

參考

《Can I Use——正則表達式》(https://caniuse.com/?search=RegExp)

《有了這 25 個正則表達式,代碼效率提高 80%》(https://juejin.cn/post/7016871226899431431)

《10 分鐘快速掌握正則表達式》 (https://www.bilibili.com/video/BV1da4y1p7iZ?spm_id_from=333.337.search-card.all.click)

看完兩件事

如果你覺得這篇內容對你挺有啓發,我想邀請你幫我兩件小事

  1. 點個「在看」,讓更多人也能看到這篇內容(點了「在看」,bug -1 😊)

  2. 關注公衆號「政採雲前端團隊」,持續爲你推送精選好文

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 90 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的 “老” 兵,也有浙大、中科大、杭電等校的應屆新人。團隊在日常的業務對接之外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推動並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

如果你想改變一直被事折騰,希望開始能折騰事;如果你想改變一直被告誡需要多些想法,卻無從破局;如果你想改變你有能力去做成那個結果,卻不需要你;如果你想改變你想做成的事需要一個團隊去支撐,但沒你帶人的位置;如果你想改變既定的節奏,將會是 “5 年工作時間 3 年工作經驗”;如果你想改變本來悟性不錯,但總是有那一層窗戶紙的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望參與到隨着業務騰飛的過程,親手推動一個有着深入的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我覺得我們該聊聊。任何時間,等着你寫點什麼,發給 ZooTeam@cai-inc.com

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eutSdSt5R-D09DXMJZaQ4A