轉載請告知並註明來源做者
做者
:唐金健
網絡暱稱
:御焱
掘金
知乎
思否
專欄
:優雅的前端
正則表達式,又稱規則表達式。(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE),計算機科學的一個概念。正則表達式一般被用來檢索、替換那些符合某個模式(規則)的文本。前端
簡單的說,就是按照某種規則去匹配符合條件的字符串。
Regexper:https://regexper.com/正則表達式
實例化
RegExp
的兩種方式。
兩種方式定義RegExp對象。segmentfault
let reg = /[a-z]{3}/gmi; let reg = /[a-z]{3}/g; let reg = /[a-z]{3}/m; let reg = /[a-z]{3}/i;
g
global 表明全局搜索。若是不添加,搜索到第一個匹配中止。m
Multi-Line 表明多行搜索。i
ignore case 表明大小寫不敏感,默認大小寫敏感。let reg = new RegExp('\\bis\\b', 'g');
由於JavaScript字符串中\
屬於特殊字符,須要轉義。數組
把元字符看成轉義字符。
正則表達式有兩種基本字符類型組成。網絡
表示本來意義上是什麼字符,就是什麼字符。函數
是在正則表達式中有特殊含義的非字母字符。 * + ? $ ^ . | \ ( ) { } [ ]
工具
字符 | 含義 |
---|---|
\t |
水平製表符 |
\v |
垂直製表符 |
\n |
換行符 |
\r |
回車符 |
\0 |
空字符 |
\f |
換頁符 |
\cX |
控制字符,與X對應的控制字符(Ctrl + X) |
相似於轉義字符。post
表示符合某種特性的字符類別。
使用元字符[]
能夠構建一個簡單的類。
所謂類是指符合某些特性的對象,一個泛指,而不是某個字符。spa
表達式[abc]
把字符a
或b
或c
歸爲一類,表達式能夠匹配這一類中的任意一個字符。prototype
// replace() 方法用於在字符串中用一些字符替換另外一些字符,或替換一個與正則表達式匹配的子串。 'a1b2c3d4e5'.replace(/[abc]/g, '0'); //010203d4e5
咱們想要替換不是abc
中任意一個字符的字符。
// 元字符 ^ 建立一個 反向類/負向類 'abcdefg'.replace(/[^abc]/g, '0'); //abc0000
匹配這一個範圍內的字符。
若是咱們想要匹配數字0-9
,那麼咱們可能會這樣寫[0123456789]
。
若是咱們想要匹配26
個字母,那麼咱們可能會這樣寫[abcdefghijklmnopqrstuvwxyz]
。
這樣略顯麻煩,因此纔會有範圍類。
// 替換全部數字 'a1c2d3e4f5'.replace(/[0-9]/g, 'x'); //axcxdxexfx // 替換全部小寫字母 'a1c2d3e4f5'.replace(/[a-z]/g, 'x'); //x1x2x3x4x5 // []組成的類內部是能夠連寫的。替換全部大小寫字母 'a1C2d3E4f5G6'.replace(/[a-zA-Z]/g, '*'); //*1*2*3*4*5*6
若是我想替換數字,而且連帶-
符號也一塊兒替換呢?
// 替換全部數字和橫槓 '2018-5-21'.replace(/[0-9-]/g, '*'); //*********
一些已經定義的類,能夠直接使用。
字符 | 等價類 | 含義 |
---|---|---|
. |
[^\r\n] |
除了回車、換行以外的全部字符 |
\d |
[0-9] |
數字字符 |
\D |
[^0-9] |
非數字字符 |
\s |
[\t\n\x0B\r] |
空白符 |
\S |
[^\t\n\x0B\r] |
非空白符 |
\w |
[a-zA-Z_0-9] |
單詞字符(字母、數字、下劃線) |
\W |
[^a-zA-Z_0-9] |
非單詞字符 |
替換一個 ab
+ 數字
+ 任意字符
的字符串
// 寫法1 'ab0c'.replace(/ab[0-9][^\r\n]/g, 'TangJinJian'); //TangJianJian // 寫法2 'ab0c'.replace(/ab\d./g, 'TangJinJian'); //TangJianJian
字符 | 含義 |
---|---|
^ |
以xxx開始(不在中括號內時的含義) |
$ |
以xxx結束 |
\b |
單詞邊界 |
\B |
非單詞邊界 |
我想替換的字符串,屬於那種只在開頭出現的。
'YuYan is a boy, YuYan'.replace(/^YuYan/g, 'TangJinJian'); //TangJinJian is a boy, YuYan
我想替換的字符串,屬於那種只在結尾出現的。
'YuYan is a boy, YuYan'.replace(/YuYan$/g, 'TangJinJian'); //YuYan is a boy, TangJinJian
單詞邊界例子。
// 替換全部is爲0 'This is a man'.replace(/is/g, '0'); //Th0 0 a man // 替換全部is前面帶有單詞邊界的字符串 'This is a man'.replace(/\bis/g, '0'); //This 0 a man // 替換全部is前面沒有單詞邊界的字符串 'This is a man'.replace(/\Bis\b/g, '0'); //Th0 is a man
用來處理連續出現的字符串。
字符 | 含義 |
---|---|
? |
出現零次或一次(最多出現一次) |
+ |
出現一次或屢次(至少出現一次) |
* |
出現零次或屢次(任意次) |
{n} |
出現n次 |
{n,m} |
出現n到m次 |
{n,} |
至少出現n次 |
我想替換字符串中連續出現10
次的數字爲*
。
'1234567890abcd'.replace(/\d{10}/, '*'); //*abcd
我想替換字符串中的QQ號碼。
'個人QQ是:10000'.replace(/[1-9][0-9]{4,}/, '19216811'); //個人QQ是:19216811
儘量多的匹配。
有這樣的一種場景下的正則表達式,/\d{3,6}/
該替換3個數字仍是6個數字呢,四、5個數字?
// 貪婪模式會盡量的往多的方面去匹配 '123456789'.replace(/\d{3,6}/, 'x'); //x789 '123456789'.replace(/\d+/, 'x'); //x '123456789'.replace(/\d{3,}/, 'x'); //x
儘量少的匹配。
若是咱們想要最低限度的替換呢?
// 非貪婪模式使用 ? 儘量的往少的方面去匹配 '12345678'.replace(/\d{3,6}?/g, 'x'); //xx78 '123456789'.replace(/\d{3,6}?/g, 'x'); //xxx
由於有g
標誌,會匹配這段字符串裏全部符合規則的字符串。
第一個規則/\d{3,6}?/g
,12345678
中有兩個符合條件的字符串,是123
和456
。因此替換結果是xx78
。
第二個規則/\d{3,6}?/g
,123456789
中有三個符合條件的字符串,是123
、456
和789
。因此替換結果是xxx
。
括號裏的一些規則,分爲一組。
我想替換連續出現3次的字母
和數字
。
//沒有分組的狀況下,後面的量詞,只是表示匹配3次數字。 'a1b2d3c4'.replace(/[a-z]\d{3}/g, '*'); //a1b2d3c4 //有分組的狀況下,分組後面的量詞,表示符合這個分組裏規則的字符串,匹配3次。 'a1b2d3c4'.replace(/([a-z]\d){3}/g, '*'); //*c4
分組裏有兩種規則,只要知足其中一種便可匹配。
//我想把ijaxxy和ijcdxy都替換成* 'ijabxyijcdxy'.replace(/ij(ab|cd)xy/g, '*'); //**
能夠把分組視爲變量,來引用。
//我想把改變年月日之間的分隔符 '2018-5-22'.replace(/(\d{4})-(\d{1,2})-(\d{1,2})/g, '$1/$2/$3'); //2018/5/22 //我想替換日期,而且更改順序 '2018-5-22'.replace(/(\d{4})-(\d{1,2})-(\d{1,2})/g, '$2/$3/$1'); //5/22/2018
忽略掉分組,不捕獲分組,只須要在分組內加上?:
// 忽略掉匹配年的分組後,匹配月的分組變成了$1,日的分組變成了$2 '2018-5-22'.replace(/(?:\d{4})-(\d{1,2})-(\d{1,2})/g, '$1/$2/$3'); //5/22/$3
正則表達式從文本頭部向尾部開始解析,文本尾部方向,稱爲「前」。
前瞻就是在正在表達式匹配到規則的時候,向前檢查是否符合斷言,後顧/後瞻方向相反。
JavaScript不支持後顧。
符合和不符合特定斷言稱爲確定/正向
匹配和否認/負向
匹配。
名稱 | 正則 | 含義 |
---|---|---|
正向前瞻 | exp(?=assert) |
|
負向前瞻 | exp(?!assert) |
|
正向後顧 | exp(?<=assert) |
JavaScript不支持 |
負向後顧 | exp(?<!assert) |
JavaScript不支持 |
有這樣一個單詞字符
+數字
格式的字符串,只要知足這種格式,就把其中的單詞字符替換掉。
'a1b2ccdde3'.replace(/\w(?=\d)/g, '*'); //*1*2ccdd*3
有這樣一個單詞字符
+非數字
格式的字符串,只要知足這種格式,就把前面的單詞字符替換掉。
'a1b2ccdde3'.replace(/\w(?!\d)/g, '*'); //a*b*****e*
global
是否全文搜索,默認false
。 ignore case
是否大小寫敏感,默認是false
。 multiline
多行搜索,默認值是false
。 lastIndex
是當前表達式匹配內容的最後一個字符的下一個位置。 source
正則表達式的文本字符串。
let reg1 = /\w/; let reg2 = /\w/gim; reg1.global; //false reg1.ignoreCase; //false reg1.multiline; //false reg2.global; //true reg2.ignoreCase; //true reg2.multiline; //true
用來查看正則表達式與指定的字符串是否匹配。返回true
或false
。
let reg1 = /\w/; reg1.test('a'); //true reg1.test('*'); //false
加上g
標誌以後,會有些區別。
let reg1 = /\w/g; // 第一遍 reg1.test('ab'); //true // 第二遍 reg1.test('ab'); //true // 第三遍 reg1.test('ab'); //false // 第四遍 reg1.test('ab'); //true // 第五遍 reg1.test('ab'); //true // 第六遍 reg1.test('ab'); //false
實際上這是由於RegExp.lastIndex
。每次匹配到以後,lasgIndex
會改變。 lastIndex
是正則表達式的一個可讀可寫的整型屬性,用來指定下一次匹配的起始索引。
let reg = /\w/g; // 每次匹配到,就會把lastIndex指向匹配到的字符串後一個字符的索引。 while(reg.test('ab')) { console.log(reg.lastIndex); } // 1 // 2
reg.lastIndex
初始時爲0
,第一個次匹配到a
的時候,reg.lastIndex
爲1
。第二次匹配到b
的時候,reg.lastIndex
爲2
。
let reg = /\w\w/g; while(reg.test('ab12cd')) { console.log(reg.lastIndex); } // 2 // 4 // 6
reg.lastIndex
初始時爲0
,第一個次匹配到ab
的時候,reg.lastIndex
爲2
。第二次匹配到12
的時候,reg.lastIndex
爲4
。第三次匹配到cd
的時候,reg.lastIndex
爲6
。
let reg = /\w/g; // 匹配不到符合正則的字符串以後,lastIndex會變爲0。 while(reg.test('ab')) { console.log(reg.lastIndex); } console.log(reg.lastIndex); reg.test('ab'); console.log(reg.lastIndex); // 1 // 2 // 0 // 1
因此,這就是爲何reg.test('ab')
再屢次執行以後,返回值爲false
的緣由了。
let reg = /\w/g; reg.lastIndex = 2; reg.test('ab'); //false
每次匹配的起始位置,是以lastIndex
爲起始位置的。上述例子,一開始從位置2
開始匹配,位置2
後面沒有符合正則的字符串,因此爲false
。
在一個指定字符串中執行一個搜索匹配。返回一個搜索的結果數組或
null
。
let reg = /\d(\w)\d/; let ts = '*1a2b3c'; let ret = reg.exec(ts); //ret是結果數組 // reg.lastIndex確定是0,由於沒有g標誌。 沒有g標誌的狀況下,lastIndex被忽略。 console.log(reg.lastIndex + '\t' + ret.index + '\t' + ret.toString()); console.log(ret); // 0 1 1a2,a // ["1a2", "a"]
返回數組是有如下元素組成的:
reg
對象的第一個子表達式相匹配的文本(若是有的話)。reg
對象的第二個子表達式相匹配的文本(若是有的話),以此類推。// 子表達式就是分組。 let reg = /\d(\w)(\w)(\w)\d/; let ts = '*1a2b3c'; let ret = reg.exec(ts); console.log(reg.lastIndex + '\t' + ret.index + '\t' + ret.toString()); console.log(ret); //輸出結果數組 // 0 1 1a2b3,a,2,b // ["1a2b3", "a", "2", "b"]
let reg = /\d(\w)(\w)(\w)\d/g; let ts = '*1abc25def3g'; while(ret = reg.exec(ts)) { console.log(reg.lastIndex + '\t' + ret.index + '\t' + ret.toString()); } // 6 1 1abc2,a,b,c // 11 6 5def3,d,e,f
第一次匹配的是1abc2
,1abc2
的後一個字符的起始位置是6
,因此reg.lastIndex
是6
。 1abc2
的第一個字符的起始位置是1
,因此ret.index
是1
。
第二次匹配的是5def3
,5def3
的後一個字符的起始位置是11
,因此reg.lastIndex
是11
。 5def3
的第一個字符的起始位置是6
,因此ret.index
是6
。
執行正則表達式和String
對象之間的一個搜索匹配。
方法返回第一個匹配項的index
,搜索不到返回-1
。
不執行全局匹配,忽略g
標誌,而且老是從字符串的開始進行檢索。
我想知道Jin
字符串的起始位置在哪裏。
'TangJinJian'.search('Jin'); //4 'TangJinJian'.search(/Jin/); //4
search
方法,既能夠經過字符串,也能夠經過正則描述字符串來搜索匹配。
當一個字符串與一個正則表達式匹配時,match()
方法檢索匹配項。
提供RegExp
對象參數是否具備g
標誌,對結果影響很大。
若是RegExp
沒有g
標誌,那麼match
只能在字符串中,執行一次匹配。
若是沒有找到任何匹配文本,將返回null
。
不然將返回一個數組,其中存放了與它找到的匹配文本有關的信息。
let reg = /\d(\w)\d/; let ts = '*1a2b3c'; let ret = ts.match(reg); console.log(ret.index + '\t' + reg.lastIndex); console.log(ret); // 1 0 // ["1a2", "a"]
非全局狀況下和RegExp.prototype.exec()
方法的效果是同樣的。
我想找到全部數字
+單詞
+數字
格式的字符串。
let reg = /\d(\w)\d/g; let ts = '*1a2b3c4e'; let ret = ts.match(reg); console.log(ret.index + '\t' + reg.lastIndex); console.log(ret); // undefined 0 // ["1a2", "3c4"]
全局狀況下和RegExp.prototype.exec()
方法的區別。在於,沒有了分組信息。
若是咱們不使用到分組信息,那麼使用String.prototype.match()
方法,效率要高一些。並且不須要寫循環來逐個全部的匹配項獲取。
使用指定的分隔符字符串將一個
String
對象分割成字符串數組。
'a,b,c,d'.split(/,/); //["a", "b", "c", "d"] 'a1b2c3d'.split(/\d/); //["a", "b", "c", "d"] 'a1b-c|d'.split(/[\d-|]/); //["a", "b", "c", "d"]
返回一個由替換值替換一些或全部匹配的模式後的新字符串。模式能夠是一個字符串或者一個正則表達式, 替換值能夠是一個字符串或者一個每次匹配都要調用的函數。
'TangJinJian'.replace('Tang', ''); //JinJian 'TangJinJian'.replace(/Ji/g, '*'); //Tang*n*an
以上兩種用法,是最經常使用的,可是還不能精細化控制。
我想要把a1b2c3d4
中的數字都加一,變成a2b3c4d5
。
'a1b2c3d4'.replace(/\d/g, function(match, index, orgin) { console.log(index); return parseInt(match) + 1; }); // 1 // 3 // 5 // 7 // a2b3c4d5
回調函數有如下參數:
match
第一個參數。匹配到的字符串。group
第二個參數。分組,若是有n個分組,則以此類推n個group
參數,下面兩個參數將變爲第2+n
和3+n
個參數。沒有分組,則沒有該參數。index
第三個參數。匹配到的字符串第一個字符索引位置。orgin
第四個參數。源字符串。我想把兩個數字之間的字母去掉。
'a1b2c3d4e5f6'.replace(/(\d)(\w)(\d)/g, function(match, group1, group2, group3, index, orgin) { console.log(match); return group1 + group3; }); // 1b2 // 3d4 // 5f6 // a12c34e56