【實踐】玩轉正則表達式+JS正則處理函數

前言

寫這篇文章不是空穴來風,最近一個禮拜寫了一個簡單的nodejs腳本,用來處理上千個文件,以便於在某些特定字符的周圍添加標記,先說一下我這個腳本使用場景主要是來識別中文(具體作什麼,以後會單獨寫一篇文章,此處只提該腳本做用),同時爲不一樣的文件類型,包括js,vm,jsp等等文件的中文周圍添加標記,以便於減小人工添加標記的事件。html

注:本文着重用示例講解,理論知識推薦此篇文章 http://www.jb51.net/tools/zhe... 最先我就是看的這篇文章初步接觸正則,正則剛開始很枯燥,慢慢學就行了,加油!!!前端

1. 正則理論部分簡介

只是看下面的這些仍是處於懵逼狀態,其實直接看第二節例子便可,爲了完整性此處便於你們查閱!node

  • [] 集合
  • {} 範圍
  • () 分組
  • | 並列關係
  • + 一次或者屢次(範圍簡寫方式1)
  • * 零次或屢次(範圍簡寫方式2)
  • ? 零次或一次(範圍簡寫方式3) 另外和貪婪模式有關,後面會講
  • ^ 兩個功能,放在開始做爲開始符,放在[]的開頭做爲「非」
  • $ 結束符
  • \ 轉義
  • (?=pattern) 正向確定環視(這個名字好多地方都不同,斷言,預查,我最喜歡環視,同下)
  • (?!pattern) 正向否認環視
  • (?<=pattern) 逆向確定環視
  • (?<!pattern) 逆向否認環視
  • . 匹配除換行符以外的全部字符(此項如下爲範圍集)
  • \s 匹配任何空白字符,包括空格、製表符、換頁符等等。
  • \S 匹配任何非空白字符(s的反面)
  • \d 匹配數字字符
  • \D 匹配非數字字符(d的反面)
  • \w 匹配字母、數字、下劃線
  • \W 匹配非字母、數字、下劃線

以上這些我以爲就是比較經常使用的基本可以夠你們使用的了,對於中文站通常還會用到中文的相關匹配,那麼中文的匹配爲[\u4e00-\u9fa5],其中\u是四個十六進制數字表示的Unicode字符,不知道匹配的中文是否全,可是大部分還都是能夠的正則表達式

2. 正則簡單調試

實際上調試正則能夠不須要什麼工具,你要非得要的話,能夠用Note Pad++,這個簡單的編輯器內置有正則的匹配,Ctrl+F彈出對話框裏邊是含有正則選擇項,打上對勾便可以在編輯其中寫內容,看看可否搜索到了。chrome

我建議看此篇文章或者練習的時候,在chrome瀏覽器直接Windows系統按F12, Mac上按command+option+J,點擊console或者控制檯,用什麼瀏覽器自行研究如何打開控制檯,如圖後端

圖片描述

光標位置便可操做js,而後利用js的就能夠展現你寫的正則是否正確,很速度的方式,能夠用上下鍵來切換,迅速修改你的表達式數組

3. JS相關處理函數

下文的Reg表明正則表達式,str表明要匹配的字符串,因爲還未開始正則表達式講解,若是有沒法理解請略讀,瞭解如何利用這幾個函數,而後開始進行第四節正則學習,而後回頭看就OK了。瀏覽器

RegExp方法

testjsp

用法:Reg.test(str)
返回值:Boolean
實例:/a/.test("a") // true編輯器

exec

用法:Reg.exec(str)
返回值:Arraynull
實例1:/b(a)/.exec("ba") // ["ba", "a", index: 0, input: "ba"]
實例2:/a(c)/.exec("ba") // null
解釋:匹配的值會按照順序-->全匹配,第一分組,第二分組...等等,匹配到的字符串位置(index),輸入的字符串(input)

String方法

match

用法:str.match(Reg)
返回值:Arraynull
其實和RegExptest方法是一致的,只不過這個字符串在前
注意:當區分模式時match返回狀況有所區別
實例1:"bababa".match(/b(a)/g) // ["ba", "ba", "ba"]
實例2:"bababa".match(/b(a)/) // ["ba", "a", index: 0, input: "bababa"]

search
用法:str.search(Reg)
返回值:Number 位置索引(無匹配返回-1)
實例:"wefeaba".search(/b(a)/) // 5

split

用法:str.split(str)str.split(Reg) 【自動全局搜索】
返回值:Array 將分開的子字符串放到數組中
實例1:"前端,後端,設計".split(",") // ["前端", "後端", "設計"]
實例2:"f4wef1er2gr".split(/\d/) // ["f", "wef", "er", "gr"]

replace

用法:str.replace(str, str)str.replace(Reg,str)str.replace(Reg,Fn)
實例1:"前端,後端,設計".replace(",", "|") // "前端|後端,設計"
實例2:"前端,後端,設計".replace(/,/, "|") // "前端|後端,設計"
實例3:"前端,後端,設計".replace(/,/g, "|") // "前端|後端|設計"
實例4:

"前端,後端,設計".replace(/,/g, function($all){
    return '{' + $all + '}';
});
// "前端{,}後端{,}設計"

實例5:

"一、這是例子balabala".replace(/一、([\u4e00-\u9fa5]+)[a-z]*/g, function($all, $1){
    return '{' + $1 + '}';
})
// "{這是例子}"

解釋: replace的函數參數順序爲-->全匹配,第一捕獲組,第二捕獲組...

4. 由淺入深講解正則

邊練習邊寫,你會發現無窮的樂趣,看到中途累了休息一下

最簡單的正則

  • /a/

用途:匹配a,只要串中包含a便可
說明:js中用兩個/來圈定正則,中間的a即爲要匹配的字符

實例:/a/.test("ab") // true

咱們如今看一個使用場景,你提供了一個輸入框,這個輸入框是讓用戶輸入手機號,先來個最簡單的規則,用戶的手機號應爲11位數字,這是一個最簡單的正則,以下所示。

  • /^\d{11}$/

用途:匹配從開始到結尾共11位數字的字符串
說明:^用來標識開頭,$用來標識結尾,\d爲數字集合,{11}表明將\d循環11次
注意:用來判斷某字符串正確與否必定要加開始結束標識符,看實例2便可看出端倪,十二位數字也被匹配上了,也就是隻要串中包含正則可匹配的就能成功,此處能夠看出開始結束符的重要性

實例1:/^\d{11}$/.test("13212344321") // true
實例2:/\d{11}/.test("132123443211") // true

  • /^\d{5,11}$/

用途:匹配從開始到結尾共5-11數字都可的字符串
說明:{5,11} 集合來標識5到11位,能夠{5,}來表示5到n多位

實例1:/^d{5,11}$/.test("1234") // false
實例2:/^d{5,11}$/.test("1234567") // true

下面繼續拓展,組合上面方式

  • /^132\d{8}$/

用途:匹配開頭爲132的手機號碼
說明:132其實就是直接匹配這三個字符,後面的其實就是動態匹配8位數字,合起來就是11位了

實例1:/^132\d{8}$/.test("18912344321") // false
實例2:/^132\d{8}$/.test("13212344321") // true

手機號不是隻有132開頭的啊,若是我想用189開頭的呢,請看

  • /^(132|189|133)\d{8}$/

用途:匹配開頭爲132或189或133的手機號
說明:此處應該注意咱們用到了分組()和並列關係|,並列就很簡單了就是說能夠132能夠189能夠133,此處必定注意分組是必定要用的若是不用就會出現實例1的狀況,由於並列關係不是前面數字了,變成了三部分了

實例1:/^132|189|133\d{8}$/.test("132") // true
實例2:/^(132|189|133)\d{8}$/.test("132") // false
實例3:/^(132|189|133)\d{8}$/.test("18912344321") // true
實例4:/^(132|189|133)\d{8}$/.test("13212344321") // true

如今來看另外一個場景,若是咱們不是判斷手機號,而是在一堆中文介紹中提取出手機號,那麼須要怎麼辦呢?

匹配內容:"你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344321}測試"

  • /\{((132|189|133)\d{8})\}/

用途:用來匹配文中的手機號,注意手機兩邊有標識{}咱們有這個定位符會很方便將其匹配出來
說明:首先,能夠看到\{,咱們前面提到了{}爲正則特殊字符,雖然此處不加也能夠,可是好習慣就是特殊字符要加上\避免出問題,例如{1},若是你要匹配的不是前面的東西循環一次那麼就會出問題了;另外,看到我用兩個()這至關於有兩個捕獲組,請看實例(這回我用exec,會看的更直接)

實例1:/\{((132|189|133)\d{8})\}/.exec("你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344321}測試")
// ["{13212344321}", "13212344321", "132", index: 21, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344321}測試"]

能夠看到第一捕獲組放在了索引爲1的位置,咱們就能夠直接取用了,不過咱們會想若是串中若是有多個電話號想搞怎麼辦,就像上面這段字符串,下面我給出js寫法,並解釋

var str = "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試";
var reg = /\{((132|189|133)\d{8})\}/g;

console.log(reg.exec(str));
console.log(reg.exec(str));

// ["{13212344321}", "13212344321", "132", index: 21, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試"]

// ["{13212344334}", "13212344334", "132", index: 41, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試"]

說明:g正則後面加個g表示全局模式;關於模式,i表示不區分大小寫,m表示多行模式,我不多用,此處不講了;對於exec有這麼個特性,當正則表達式爲全局匹配模式每次執行exec後會刷新下一次執行開始位置,下一次的開始位置爲第一次匹配的最後一個字符的下一個位置,因此執行兩次就會將串中全部的匹配出來,這樣就實現了提取的目的

繼續看上面的這段文本,若是我想匹配jackwang怎麼辦呢?

  • /[acgjknw]+/

用途:匹配包含acgjknw這些字符的1或屢次循環
說明:[]是字符集,裏邊的就是要表示的字符,後面加一個+那麼就是表示將前面的[]裏邊的循環1次或屢次,同理?*再也不用例子展現了

實例:/[acgjknw]+/g.exec("你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試")
// ["jackwang", index: 6, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試"]

能夠看到咱們寫了不少字母,其實咱們想匹配英文名,英文名不必定只有這幾個字符,因此此處咱們能夠這樣

  • /[a-z]+/

用途:匹配包含a-z的1或屢次循環
說明:注意-這是範圍的意思,按照ASCII中的順序,寫這個範圍就行,這回我搞腳本的時候就遇到一個坑,如實例2,原本想匹配:-=,可是忘記對-進行轉義,致使<也被匹配上

實例1:/[a-z]+/g.exec("你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試")
// ["jackwang", index: 6, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲{13212344334}測試"]
實例2:/[:-=]/.test("<")// true

還有一些什麼沒講呢,下面單獨舉例來講明

貪婪模式/非貪婪模式
字符串:"baeabaeab"

  • /b[a-z]+b//b[a-z]+?b/

說明:前者爲貪婪模式,後者爲非貪婪模式;請注意+後面的?添加了就爲非貪婪模式,同理*和'?'後面能夠添加;貪婪模式就是屢次循環會盡量的去匹配,非貪婪模式就是最少匹配,看實例結果便可明白,此正則功能頗有用但願你們記住

實例1:/b[a-z]+b/.exec("baeabaeab")
// ["baeabaeab", index: 0, input: "baeabaeab"]
實例2:/b[a-z]+?b/.exec("baeabaeab")
// ["baeab", index: 0, input: "baeabaeab"]

反義

  • /[^5]/

說明:只要在集合的最開始用^便可,就是表示除了5以外全部字符

實例:/[^5]/.test("5") // false

環視

環視其實某些狀況仍是挺好用的,還記得前面的匹配手機號嗎?

字符串: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲13212344334測試"

其實咱們還能夠用環視

  • /(?<=\{)((132|189|133)\d{8})(?=\})/

實例:/(?<=\{)((132|189|133)\d{8})(?=\})/.exec("你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲13212344334測試")
// ["13212344321", "13212344321", "132", index: 22, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲13212344334測試"]

說明:剛開始環視可能比較難以理解,我通俗點講,我以爲比我最開始提到的文章好理解

  1. 這就像站隊同樣,我在一排中想要找出你來,就能夠直接匹配,可是我要找出張三和李四中間的你,可是我只想要你,不要帶着他倆,那麼環視就是這個功能
  2. 環視被包含的東西是不參與到正則最後匹配出來的東西當中的,它只起到一個定位的做用
  3. (?<=\{)逆向確定環視,舉例來講,在匹配132以前要從1往回看,也就是逆向看看他的上一位是否是{,若是是的話纔算合法,若是不是就匹配不成功,也就是你要往前看不是張三,那我就不找你了。同理,(?=\})正向確定環視,也就是匹配手機號以後,後面必定要有},這樣纔算成功,也就是你向後(順序)看是李四才行,這整個表達式也就是你向前(逆向)看必須是張三,向後(正向)看必須是李四,我纔會找你。
  4. 上面兩個是確定狀態,另外還有兩個否認狀態,意思就是反的,(?<!pattern)逆向否認環視(?!pattern)正向否認環視, pattern爲要匹配的表達式,舉個例子
  • /(?<!\{)((132|189|133)\d{8})(?!\})/

說明:匹配手機號前面沒有{後面沒有}的手機號

實例2:/(?<!\{)((132|189|133)\d{8})(?!\})/.exec("你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲13212344334測試")
// ["13212344334", "13212344334", "132", index: 41, input: "你們好,我叫jackwang,個人手機號是{13212344321},他的手機號爲13212344334測試"]

綜上,看出來這兩個實例的不一樣了嗎,固然你能夠任意匹配這四種方式來達到你要的效果;可是注意,可能每種語言支持程度不一樣,不要過分依賴。

5. 其餘

匹配任意字符建議使用 /[\s\S]+/由於.會去掉換行符
注意用到的特殊字符必定要轉義(好習慣) /[\{\}\[\]\^\$]/
注意-的使用 /[:-=]/ 這樣會匹配<等,注意轉義

運算符優先級

運算符 描述
\ 轉義
()[] 圓括號和方括號
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, 任何元字符、任何字符 定位點和序列
| 或操做

關於那些js的處理函數,本身去摸索吧,篇幅這麼長,估計要看不下去了,我搞腳本的時候大量使用了replacetest函數,很好用,對於文件總體處理操做很好用,建議好好學學,請見MDN:https://developer.mozilla.org...

可能還有其餘一些沒講到的正則知識,可是上面的這些基本包含了正則的90%了。

總結

囉裏囉嗦講了這麼久,只是爲了讓新手可以由淺入深的慢慢學習,學習正則不是一蹴而就的過程,須要慢慢使用,慢慢探索,同一個匹配可能能寫出好多正則,你們能夠慢慢練習,寫出更優雅的正則。

另外要說一點,不是非要寫一個巨長的正則來匹配巨難的字符串,要合理利用各語言的函數來簡化正則的寫法,不然一個巨長的正則可能就是噩夢,這個事本身權衡,相信會將正則用到極致。

謝謝!

本文若有疏漏之處或者又問題交流,請直接回複本文!

相關文章
相關標籤/搜索