整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          詳解JavaScript中的RegExp:深入理解正

          詳解JavaScript中的RegExp:深入理解正則表達式

          在JavaScript中,正則表達式(Regular Expression,簡稱RegExp)是一種強大的文本處理工具,它提供了查找、替換和提取字符串中模式匹配項的強大功能。本文將帶領您深入理解JavaScript中的正則表達式,并通過實例演示其常見用法與高級特性。

          1. 創建正則表達式

          在JavaScript中創建一個正則表達式有兩種方式:

          • 字面量形式:
          let regex=/pattern/flags;

          例如,查找所有的數字:

          let numbersRegex=/\d+/;
          • 構造函數形式:
          let regex=new RegExp('pattern', 'flags');

          同樣的例子,使用構造函數形式:

          let numbersRegex=new RegExp("\d+", "");

          注意:在構造函數中寫入字符串時,需要對特殊字符進行轉義。

          2. 正則表達式模式

          正則表達式的主體部分是“模式”,用于描述要匹配的文本結構。以下是一些基本模式示例

          • \d 匹配任何數字(等同于 [0-9])
          • \w 匹配字母、數字或下劃線(等同于 [a-zA-Z0-9_])
          • \s 匹配任何空白字符(包括空格、制表符、換行符等)
          • . 匹配除換行符之外的任意單個字符
          • ^ 匹配字符串的開始位置
          • $ 匹配字符串的結束位置
          • * 表示前面的元素可以重復零次或多次
          • + 表示前面的元素至少重復一次
          • ? 表示前面的元素可選,出現零次或一次
          • {n} 表示前面的元素必須重復n次
          • {n,} 表示前面的元素至少重復n次
          • {n,m} 表示前面的元素重復次數在n到m之間

          3. 標志位

          標志位位于正則表達式末尾,用于改變匹配行為:

          • g 全局搜索,匹配字符串中的所有符合條件的位置,而不是只找到第一個就停止。
          • i 忽略大小寫。
          • m 多行模式,使^和$可以匹配每一行的開始和結束。

          4. 正則方法應用

          在JavaScript中,我們可以使用以下內置的方法來操作正則表達式:

          • test():檢測字符串是否符合某個正則表達式。
          let isNumber=/\d+/.test("123");
          console.log(isNumber); // true
          • match():在字符串上執行查找,返回匹配結果數組或者null。
          let matches="Hello 123 World".match(/\d+/);
          console.log(matches[0]); // "123"
          • exec():在字符串上執行查找,返回包含匹配信息的對象或者null。
          let reg=/\d+/g;
          let matchResult=reg.exec("Hello 123 World");
          console.log(matchResult[0]); // "123"
          • search():返回字符串中第一個匹配項的索引,如果沒有找到則返回-1。
          let index="Hello 123 World".search(/\d+/);
          console.log(index); // 6
          • replace():使用新的子串替換與正則表達式匹配的子串。
          let replacedStr="Hello 123 World".replace(/\d+/, "456");
          console.log(replacedStr); // "Hello 456 World"
          • split():根據正則表達式分隔字符串,返回一個由分割后的子串組成的數組。
          let words="Hello World, How are you?".split(/\s*,\s*/);
          console.log(words); // ["Hello World", "How are you?"]

          5. 高級技巧

          • 非捕獲組 (?:...):不捕獲括號內的內容,僅作分組之用。
          • 負向前瞻 (?=...) 負向后瞻 (?!...):用來斷言后面或前面的內容,但并不包含在匹配結果內。
          • 反向引用 \n:匹配第n個括號里的內容。
          let regex=/(hello)\s+(world)(?=\!)/;
          let str="hello world!";
          let match=str.match(regex);
          console.log(match[1]); // "hello"
          console.log(match[2]); // "world"

          6.小結

          JavaScript中的正則表達式功能強大且靈活,熟練掌握這一工具能極大地提高開發效率,尤其是在數據清洗、驗證和搜索等場景。希望這篇指南能幫助你更好地理解和運用正則表達式,實際操作中還需結合具體需求進行調整和優化。

          片來源 | 電影《成長教育》劇照

          正則表達式是描述一組字符串特征的模式,用來匹配特定的字符串。” ——Ken Thompson

          什么是正則表達?

          正則表達式其實就是一種pattern、一種模式、一種格式、一種規則,它主要是用在文本字符串處理的時候,想要在一堆文本中找到滿足某種格式、規則的字符串

          它起源于上個20世紀50年代科學家在數學領域做的一些研究工作,后來才被引入到計算機領域中。從它的命名我們可以知道,它是一種用來描述規則的表達式。

          比如,你要寫一個應用, 想設定一個用戶命名的規則, 讓用戶名包含字符,數字,下劃線和連字符,以及限制字符的個數,好讓名字看起來沒那么丑。那么,以下正則表達式來驗證一個用戶名:


          以上的正則表達式可以接受 johndoe, jo-hndoe, john12_as. 但不匹配Jo, 因為它包含了大寫的字母而且太短了。

          那么,要想學正則表達試,初學者應該從哪些方面入手?

          這里強烈安利的幾個學習教程和資源:

          1.正則表達式30分鐘入門教程

          http://deerchao.net/tutorials/regex/regex.htm

          推薦理由:特別適合想要快速入門的同學,結合實例可以讓你理解基本的原理和語法。

          2.高效入門正則表達式

          https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md

          推薦理由:Github上一篇簡單的正則表達式教程,提供了十幾種語言(包括中文),這篇教程覆蓋到了你在實際應用中99%的場景。

          3.regex101網站——能可視化展示正則匹配結果

          https://regex101.com


          4.regexper網站——能夠直觀展示正則表達式的狀態機圖

          https://regexper.com

          正則的進階——從底層工作機制來理解正則表達式

          很多同學在入門某個語言或工具時,總是習慣先從實例上手學習,往往忽略了對語言最底層的一些原理,這里引用@小胡子哥的“進階正則表達式”一文,來幫助你更好的熟悉和運用正則表達式。

          注:此次引用,在原文基礎上做了刪減,想看完整內容,請轉至,

          https://www.cnblogs.com/hustskyking/p/how-regular-expressions-work.html

          正則表達式的工作機制

          畫了一個草圖,簡單的說明了下正則表達式的工作原理。

           | 編譯 |
           +--------+
           |
           ↓
          +----------------+
          | 設置開始位置 |←---------+
          +----------------+ ↑
           | |
           ↓ 其 |
          +----------------+ 他 |
          | 匹配 & 回溯 | 路 |
          +----------------+ 徑 |
           | |
           ↓ |
          +----------------+ |
          | 成功 
          or
           失敗 |---------→+
          +----------------+
          

          你寫的任何一個正則直接量或者 RegExp 都會被瀏覽器編譯為一個原生代碼程序。第一次匹配是從頭個字符開始,匹配成功時,他會查看是否還有其他的路徑沒有匹配到,如果有的話,回退到上一次成功匹配的位置,然后重復第二步操作,不過此時開始匹配的位置(lastIndex)是上次成功位置加 1.這樣說有點難以理解,下面寫了一個 demo,這個 demo 就是實現一個正則表達式的解析引擎,因為邏輯和效果的表現都太復雜了,所以只做了一個簡單的演示:

          Reg:

          /H(i|ello), barret/g
          

          Str:

          Lalala. Hi, barret. Hello, John
          

          如果上面的 demo 跑不起來,請戳這里:

          http://qianduannotes.duapp.com/demo/regexp/index.html

          如果要深入了解正則表達式的內部原理,必須先理解匹配過程的一個基礎環節——回溯。他是驅動正則的一個基本動力,也是性能消耗、計算消耗的根源。

          回溯

          正則表達式中出現最多的是分支和量詞。上面的 demo 中可以很清楚的看到 hi 和 hello 這兩個分支,當匹配到第一個字符 h 之后,進入 (i | ello) 的分支選擇,首先是進入 i 分支,當 i 分支匹配完了之后,再回到分支選擇的位置,重新選擇分支。簡單點說,分支就是 | 操作符帶來的多項選擇問題,而量詞指的是諸如 *,+?,{m,n} 之類的符號,正則表達式必須決定何時嘗試匹配更多的字符。下面結合回溯詳細說說分支和量詞。

          1.分支

          繼續分析上面那個案例。 "Lalala. Hi, barret. Hello, John".match(/H(i|ello), barret/g),首先會查找 H 字符,在第九位找到 H 之后,正則子表達式提供了兩個選擇 (i|ello),程序會先拿到最左邊的那個分支,進入分支后,在第十位匹配到了 i,接著匹配下一個字符,下一個字符是逗號,接著剛才的位置又匹配到了這個逗號,然后再匹配下一個,依次類推,直到完整匹配到整個正則的內容,此時程序會在 Hi,barret后面做一個標記,表示在這里進行了一次成功的匹配。但程序到此并沒有結束,因為后面加了一個全局參數,依然使用這個分支往后匹配,很顯然,到了 Hello 的時候,Hi 分支匹配不了了,于是程序會回溯到剛才我們做標記的位置,并進入第二個分支,從做標記的位置重新開始匹配,依次循環。

          只要正則表達式沒有嘗試完所有的可選項,他就會回溯到最近的決策點(也就是上次匹配成功的位置)。

          2.量詞

          量詞這個概念特別簡單,只是在匹配過程中有貪婪匹配和懶惰匹配兩種模式,結合回溯的概念理解稍微復雜。還是用幾個例子來說明。

          1)貪婪

          str="AB1111BA111BA";
          reg=/AB[\s\S]+BA/;
          console.log(str.match(reg));
          

          首先是匹配AB,遇到了 [\s\S]+,這是貪婪模式的匹配,他會一口吞掉后面所有的字符,也就是如果 reg 的內容為 AB[\s\S]+,那后面的就不用看了,直接全部匹配。而往后看,正則后面還有B字符,所以他會先回溯到倒數第一個字符,匹配看是否為 B,顯然倒數第一個字符不是B,于是他又接著回溯,找到了B字母,找到之后就不繼續回溯了,而是往后繼續匹配,此刻匹配的是字符A,程序發現緊跟B后的字母確實是A,那此時匹配就結束了。如果沒有看明白,可以再讀讀下面這個圖:

          REG: 
          /AB[\s\S]+BA/
          MATCH: A 匹配第一個字符
           AB 匹配第二個字符
           AB1111BA111BA [\s\S]+ 貪婪吞并所有字符
           AB1111BA111BA 回溯,匹配字符B
           AB1111BA111B 找到字符B,繼續匹配A
           AB1111BA111BA 找到字符A,匹配完成,停止匹配
          

          2) 懶惰(非貪婪)

          str="AB1111BA111BA";
          reg=/AB[\s\S]+?BA/;
          console.log(str.match(reg));
          

          與上面不同的是,reg 中多了一個 ? 號,此時的匹配模式為懶惰模式,也叫做非貪婪匹配。此時的匹配流程是,先匹配AB,遇到[\s\S]+?,程序嘗試跳過并開始匹配后面的字符B,往后查看的時候,發現是數字1,不是要匹配的內容,繼續往后匹配,知道遇到字符B,然后匹配A,發現緊接著B后面就有一個A,于是宣布匹配完成,停止程序。

           REG: 
          /AB[\s\S]+BA/
          MATCH: A 匹配第一個字符
           AB 匹配第二個字符
           AB [\s\S]+? 非貪婪跳過并開始匹配B
           AB1 不是B,回溯,繼續匹配
           AB11 不是B,回溯,繼續匹配
           AB111 不是B,回溯,繼續匹配
           AB1111 不是B,回溯,繼續匹配
           AB1111B 找到字符B,繼續匹配A
           AB1111BA 找到字符A,匹配完成,停止匹配
          

          如果匹配的內容是 AB1111BA,那貪婪和非貪婪方式的正則是等價的,但是內部的匹配原理還是有區別的。為了高效運用正則,必須搞清楚使用正則時會遇到那些性能消耗問題。

          逗比的程序

          //去測試下這句代碼
          "TTTTTTTT".match(/(T+T+)+K/);
          //然后把前面的T重復次數改成30
          //P.S:小心風扇狂轉,CPU暴漲
          

          我們來分析下上面這段代碼,上面使用的都是貪婪模式,那么他會這樣做:

          REG: (T+T+)+K
          MATCH: ①第一個T+匹配前7個T,第二個T+匹配最后一個T,沒找到K,宣布失敗,回溯到最開始位置
           ②第一個T+匹配前6個T,第二個T+匹配最后兩個T,沒找到K,宣布失敗,回溯到最開始位置
           ③...
           ... 接著還會考慮(T+T+)+后面的 + 號,接著另一輪的嘗試。
           ⑦...
           ...
          

          這段程序并不會智能的去檢測字符串中是否存在 K。如果匹配失敗,他會選擇其他的匹配方式(路徑)去匹配,從而造成瘋狂的回溯和重新匹配,結果可想而知。這是回溯失控的典型例子。

          前瞻和反向引用

          1.前瞻和引用

          前瞻有兩種。一種是負向前瞻,JS中使用 (?!xxx) 來表示,他的作用是對后面要匹配的內容做一個預判斷,如果后面的內容是xxx,則此段內容匹配失敗,跳過去重新開始匹配。另一種是正向前瞻,(?=xxx),匹配方式和上面相反,還有一個長的類似的是 (?:xxx),這個是匹配xxx,他是非捕獲性分組匹配,即匹配的內容不會創建反向引用。具體內容可以去文章開頭提到的文檔中查看。

          反向引用,這個在 replace 中用的比較多,在 replace 中:

          而在正則表達中,主要就是 , 之類的數字引用。前瞻和反向引用使用恰當可以大大的減少正則對資源的消耗。舉個例子來簡單說明下這幾個東西:

          問題:使用正則匹配過濾后綴名為 .css 和 .js 的文件。

          如:test.wow.js test.wow.css test.js.js等等。

          有人會立馬想到使用負向前瞻,即:

          //過濾js文件
          /(?!.+\.js$).*/.exec("test.wow.js")
          //過濾js和css文件
          /(?!.+\.js$|.+\.css$).*/.exec("test.wow.js")
          /(?!.+\.js$|.+\.css$).*/.exec("test.wow.html")
          

          但是你自己去測試下,拿到的結果是什么。匹配非js和非css文件可以拿到正確的文件名,但是我們期望這個表達式對js和css文件的匹配結果是null,上面的表達式卻做不到。問題是什么,因為(?!xxx)和(?=xxx)都會消耗字符,在做預判斷的時候把 .js 和 .css 給消耗了,所以這里我們必須使用非捕獲模式。

          /(?:(?!.+\.js$|.+\.css$).)*/.exec("test.wow.html");
          /(?:(?!.+\.js$|.+\.css$).)*/.exec("test.wow.js");
          

          我們來分析下這個正則:

          (?:(?!.+\.js$|.+\.css$).)*
          --- ---------------- -
          | | |
          +----------------------+
           ↓ |
          非捕獲,內部只有一個占位字符
           |
           ↓
           負向前瞻以.js和.css結尾的字符串
          

          最后一個星號是貪婪匹配,直接吞掉全部字符。

          這里講的算是有點復雜了,不過在稍復雜的正則中,這些都是很基礎的東西了,想在這方面提高的童鞋可以多研究下。

          2.原子組

          JavaScript的正則算是比較弱的,他沒有分組命名、遞歸、原子組等功能特別強的匹配模式,不過我們可以利用一些組合方式達到自己的目的。上面的例子中,我們實際上用正則實現了一個或和與的功能,上面的例子體現的還不是特別明顯,再寫個例子來展示下:

          str1="我(wo)叫(jiao)李(li)靖(jing)";
          str2="李(li)靖(jing)我(wo)叫(jiao)";
          reg=/(?=.*?我)(?=.*?叫)(?=.*?李)(?=.*?靖)/;
          console.log(reg.test(str1)); //true
          console.log(reg.test(str2)); //true
          

          不管怎么打亂順序,只要string中包含“我”,“是”,“李”,“靖”這四個字,結果都是true。

          類似(?=xxx),就相當于一個原子組,原子組的作用就是消除回溯,只要是這種模式匹配過的地方,回溯時都不會到這里和他之前的地方。上面的程序 "TTTTTTTT".match(/(T+T+)+K/);可以通過原子組的方式處理:

          "TTTTTTTT".match(/(?=(T+T+))\2+K/);
          

          如此便能徹底消除回溯失控問題。

          - 完 -


          與其他程序設計語言一樣,學習正則表達式的關鍵是實踐,實踐,再實踐。 ——本?福塔(Ben Forta)

          [美] 本?福塔(Ben Forta)著 門佳 楊濤 等 (譯)

          • 緊貼實戰需求,化繁為簡,高效解決編程難題
          • 如果想快速上手正則表達式,那么除了本書,沒有第二種選擇
          • 相比上一版,書中完善了正則表達式的用法,豐富了提示、注意、警告等信息。

          本書從簡單的文本匹配開始,循序漸進地介紹了很多復雜內容,包括反向引用、條件評估、環視等。每章都配有許多簡明實用的示例,有助于全面、系統、快速掌握正則表達式,并運用它們解決實際問題。

          做項目或者代碼編寫過程中,一般會遇到驗證電話、郵箱等格式是否正確合法的問題。通常我們會使用正則表達式,自己寫很麻煩,且正則表達式又不是那么容易記住。所以現在分享幾條常用的正則表達式,需要的時候直接復制即可。

          // uri正則表達式
          const urlRegex=/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
          // 郵箱正則表達式
          const emailRegex=/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/;
          // 手機號碼正則表達式
          const mobileRegex=/^1[0-9]{10}$/;
          // 電話號碼正則表達式
          const phoneRegex=/^([0-9]{3,4}-)?[0-9]{7,8}$/;
          // URL地址正則表達式
          const urlRegex=/^http[s]?:\/\/.*/;
          // 小寫字母正則表達式
          const lowerCaseRegex=/^[a-z]+$/;
          // 大寫字母正則表達式
          const upperCaseRegex=/^[A-Z]+$/;
          // 大小寫字母正則表達式
          const alphabetsRegex=/^[A-Za-z]+$/;
          // 身份證號碼正則表達式
          const cardidRegex=/(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
          // 判斷姓名是否正確
          const regNameRegex=/^[\u4e00-\u9fa5]{2,4}$/;
          // 判斷是否為整數
          const numRegex=/[^\d.]/g;
          // 判斷是否為小數
          const numordRegex=/[^\d.]/g;
          // 數字或者小數
          const numberRegex=/^[0-9]+(\.[0-9]{1,3})?$/;

          如果大家有其它常用的正則表達式,可以分享在評論區!


          主站蜘蛛池模板: 无码人妻aⅴ一区二区三区| 日本一区二区三区在线视频| 美女免费视频一区二区三区| 免费看一区二区三区四区 | 精品一区二区三区四区| 国产精品一区二区三区99 | 日本人真淫视频一区二区三区| 亚洲精品无码一区二区| 免费高清在线影片一区| 亚洲AV无码国产精品永久一区| 日韩精品无码一区二区三区免费| 精品福利一区二区三| 激情内射亚洲一区二区三区爱妻 | 亚洲熟妇av一区二区三区| 日韩一区二区在线观看| 福利一区在线视频| 亚洲日韩中文字幕无码一区| 国产剧情国产精品一区| 久久精品一区二区东京热| 亚洲一区日韩高清中文字幕亚洲| 精品一区二区久久久久久久网精| 男人的天堂av亚洲一区2区| 2021国产精品视频一区| 国产精品自在拍一区二区不卡| 成人无码AV一区二区| 亚洲国产激情一区二区三区 | 天码av无码一区二区三区四区 | 中文字幕日韩人妻不卡一区| 制服美女视频一区| 中文激情在线一区二区| 78成人精品电影在线播放日韩精品电影一区亚洲 | V一区无码内射国产| 男人的天堂亚洲一区二区三区 | 亚洲成AV人片一区二区密柚| 中文字幕一区二区人妻性色| 日韩国产一区二区| 精品国产高清自在线一区二区三区| 精品在线视频一区| 中日韩精品无码一区二区三区| 台湾无码一区二区| 亚洲bt加勒比一区二区|