做爲一名合格的前端開發工程師,瞭解並掌握正則表達式是很是有必要的。多年的項目經驗告訴我,學好正則表達式可讓咱們少寫不少的代碼。這篇文章很是適合哪些初中級和對正則表達式掌握不是很清楚的同窗,廢話很少少,我門開始吧。。。javascript
js中的正則表達式用 RegExp
對象表示,可使用 RegExp()
構造函數來建立 RegExp
對象,不過更多狀況下咱們使用一種特殊的直接量語法來建立,舉個例子:前端
// 使用直接量的形式
var pattern = /\d$/;
// 使用構造函數的形式
var pattern = new RegExp('\d$');
複製代碼
可是若是須要咱們動態的生成一個正則表達式呢,那麼這個時候就須要使用構造函數形式了, 看下面的這個例子java
var fmt = 'MM-dd hh:mm:ss';
var date = new Date();
var fmtObj = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
}
for(let key in fmtObj) {
if (new RegExp(`(${key})`).test(fmt)) {
fmt = fmt.replace(RegExp.$1, RegExp.$1.lengt === 1 ? fmtObj[key] : ('00' + fmtObj[key]).substr((fmtObj[key] + '').length))
}
}
複製代碼
正則表達式中全部的字母和數字都是按照字面量的含義進行匹配的,js的正則表達式也支持非字母的字符匹配,這些字符須要功過反斜槓 \
進行轉譯。下表中列舉了轉譯字符正則表達式
\o | NUL字符(\u0000) |
\t | 製表符(\u0009) |
\n | 換行符 (\u000A) |
\v | 垂直製表符(\u000B) |
\f | 換頁符(\u000C) |
\r | 回車符(\u000D) |
\xnn | 由16進制數nn指定的拉丁字符,例如,\x0A等價於\n |
\uxxxx | 由16進制數xxxx指定的unicode字符,例如\u0009等價於\t |
\cX | 控制字符^X,\cJ等價於換行符\n |
字母和數字字符 | 匹配自身 |
在正則表達式中,許多標點符號具備特殊的含義,他們是:^ $ . * + ? = ! : | \ / ( ) { } [ ]
數組
某些符號只能在正則表達式的某些上下文中才具備某種特殊含義,在其餘上下文中則會被當成直接量處理。若是想要在正則表達式中使用這些符號的直接量進行匹配,則必須使用前綴 \
,這是一條通用的規則,其餘標點符號(例如@和引號)沒有特殊含義,在正則表達式中按照字面量含義進行匹配,若是不記得哪些標點符號須要反斜杆轉義,能夠在每個標點符號前面都加上轉義符 \
。另外須要注意,許多字母和數字在有反斜杆作前綴時會有特殊含義,因此對於想按照直接量進行匹配的數字和字母,儘可能不要用 \
進行轉義函數
將直接量字符單獨放進方括號內就組成了字符類。一個字符類能夠匹配它所包含的任意字符,所以表達式 /[abc]/
就和字母a,b,c中的任意一個都匹配ui
var reg = /[a-z0-9]/;
複製代碼
上面的正則表達式能夠匹配任意一個字母或數字,其中 a-z
表示a到z的全部字母,0-9
表示0到9之間的數字spa
就是匹配字符類中不含有的字符,用 ^
符號開頭code
var reg = /[^a-z0-9]/;
複製代碼
上面的正則表達式的意思就是匹配任意一個不含有字母和數字的字符regexp
[...] | 字符類 |
[^...] | 反字符類 |
. | 除換行符和其餘unicode行終止符以外的任意字符 |
\w | 數字,字母和下劃線, 等價於[a-z0-9_] |
\W | 非數字,字母和下劃線, 等價於[^a-z0-9_] |
\s | 任何unicode 空白符 |
\S | 非任何unicode 空白符 |
\d | 數字 |
\D | 非數字 |
\b | 單詞邊界 |
\B | 非單詞邊界 |
{n, m} | 最少匹配n次,最多匹配m次 |
{n, } | 至少匹配n次或更多 |
{n} | 匹配n次 |
? | 匹配0次或一次 |
+ | 最少匹配一次 |
* | 匹配0次或屢次 |
上面咱們列舉出了匹配重複字符是儘量多的匹配,並且容許後續的正則表達式繼續匹配。所以咱們稱之爲'貪婪'匹配模式。咱們一樣可使用的正則表達式進行非貪婪匹配。只須要在待匹配的字符後跟隨一個問號便可: '??', '+?', '*?' 或者'{1, 5}?'
var a = /[a-z0-9]+?/
a.exec('sdfff900') // ["s", index: 0, input: "sdfff900", groups: undefined]
// 使用非貪婪模式匹配獲得的結果可能和指望並不同,能夠來看看看下面的這個例子:
var b = /a+?b/;
b.exec('aaab') // ["aaab", index: 0, input: "aaab", groups: undefined]
// 下面這樣的匹配結果你想到了嗎??
var c = /shen(ab)+?/;
b.exec('shenababab'); // ["shenab", "ab", index: 0, input: "shenababab", groups: undefined]
複製代碼
這是由於正則表達式的模式匹配老是會尋找字符串中第一個可能匹配的位置,因爲這個匹配是從字符串的第一個字符開始的,所以在這裏不考慮他的子串中更短的匹配
正則表達式語法還包括指定選擇項,子表達分組和引用前一子表達式的特殊字符,咱們分別來看一下
|
用於分隔供選擇的字符。/ab|cd|ef/
,能夠匹配字符串ab,也能夠匹配cd或者是ef。
注意選擇項的嘗試匹配的次序是從左到右,直到發現了匹配的項。若是左邊的選擇項匹配,就忽略右邊的選擇項,即便產生更好的匹配。 因此,當正則表達式 /a|ab/
匹配字符串 ab
時,他只能匹配第一個字符串 a
正則表達式中的圓括號有多種做用。
| * + ?
等來對單元內的項進行處理。var a = /java(script)?/; // 能夠匹配字符串 'java',其後能夠有'script'也能夠沒有。
var b = /(ab|cd)+|ef/; // 能夠匹配字符串ef, 也能夠匹配ab或者cd的一次或屢次重複
複製代碼
var a = /[a-z]+(\d+)/; // 那麼這個時候咱們能夠從檢索到的匹配中拿到和圓括號中子模式相匹配的數字
複製代碼
()
表示捕獲分組,()
會把每一個分組裏的匹配的值保存起來,使用 $n
(n是一個數字,表示第n個捕獲組的內容)
var reg = /\b(shen(sxx))/;
reg.test('shensxx');
console.log(RegExp.$1, RegExp.$2) // shensxx sxx
reg.exec('shensxx');
// ["shensxx", "shensxx", "sxx", index: 0, input: "shensxx", groups: undefined]
複製代碼
(?:)
表示非捕獲分組,和捕獲分組惟一的區別在於,非捕獲分組匹配的值不會保存起來
var reg = /\b(shen(?:sxx))/;
reg.test('shensxx');
console.log(RegExp.$1, RegExp.$2) // shensxx
reg.exec('shensxx');
// ["shensxx", "shensxx", index: 0, input: "shensxx", groups: undefined]
複製代碼
這是經過在字符 \
後面加一位數字或多位數字來實現的,這個數字指定了圓括號的子表達式在正則表達式中的位置。\1, \3
分別表示正則表達式中的第一個圓括號的子表達式和第三個子表達式(注意這裏指的是自表達式中匹配的文本的引用,並非指子表達式)
注意: 由於子表達式能夠嵌套另外一個子表達式,因此他們的位置是參與計數的左括號的位置
var a = /([Jj]ava([Ss]cript)?)\sis\s(fun\w*)/; // \2 則是表明的([Ss]sript)
var b = /(['"])[^'"]*\1/; // 只能匹配單引號或者雙引號是成對出現的,不容許出現一個單引號一個雙引號
複製代碼
有一些正則表達式匹配的是字符串之間的位置,而不是實際的字符。例如 \b
匹配一個單詞邊界,即位於 \w
和 \W
之間的邊界,或者是一個字符串開始或結束的位置。像 \b
這樣的元素不匹配某個可見的字符,他們指定匹配發生的合法位置。還有一些錨元素 ^ $
, 分表表示匹配字符串開始和結束的位置
\b
匹配一個單詞邊界,即位於 \w
和 \W
之間的邊界,或者是一個字符串開始或結束的位置。var reg = /xx\b/;
reg.test('sxx shen') // true
reg.test('xxs shen') // false
var reg = /\bsx/;
reg.test('sxx shen') // true
reg.test('xsx shen') // false
var reg = /\b\d+/;
reg.test('.123') // true
reg.test('sxx124') // false
複製代碼
\B
匹配非單詞邊界。er\B
能匹配 verb
中的 er
,但不能匹配 never
中的 er
。x(?=y)
:正向先行斷言,匹配 x
僅僅當 x
後面不跟着 y
x(?!y)
:負向先行斷言, 匹配 x
僅僅當 x
後面不跟着 y
咱們看下面的正則表達式,意思就是非單詞邊界後面跟這三個數字字符串,並且三個數字字符串後面再也不跟數字
var reg = /\B(?=(\d{3})+(?!\d))/g;
'123456789.123456'.replace(reg, ',') // "123,456,789.123,456"
複製代碼
(?<=y)x
:正向後行斷言,匹配 x
僅僅當 x
前面跟着 y
var reg = /(?<=95|98|NT|2000)Windows/;
'3.1Windows'.replace(reg, 'aaaa'); // '3.1Windows'沒有匹配到
'2000Windows'.replace(reg, 'aaaa'); // '2000aaaa'
複製代碼
(?<!y)x
:負向後行斷言,匹配 x
僅僅當 x
前面跟的不是 y
var reg = /(?<!95|98|NT|2000)Windows/;
'3.1Windows'.replace(reg, 'aaaa'); // '3.1aaaa'
'2000Windows'.replace(reg, 'aaaa'); // '2000Windows' 沒有匹配到
複製代碼
var reg = /(?<!\.\d*)\B(?=(\d{3})+(?!\d))/g
'123456789.98764525437'.replace(reg, ',')
// "123,456,789.98764525437"
複製代碼
i: 執行時不區分大小寫 g: 執行一個全局匹配,簡而言之,就是找到全部的匹配,而不是在找到第一個以後就中止 m: 多行匹配模式,^
匹配一行的開頭和字符串的開頭,$
匹配行的結束和字符串的結束
使用字面量形式時: /\bjavascript\b/ig
構造函數形式: new RegExp('\bjavascript\b', 'ig')
@params: 一個正則表達式,若是參數不是一個正則表達式,那麼會經過 `RegExp` 構造函數轉成正則表達式
@return: 第一個與之匹配的字符串的起始位置,如果沒有發生匹配就返回數字 `-1`
複製代碼
var a = 'javascript'
'fsfsdjavsssjavasdfsfsjfjhhshh3r98u'.search(a) // 11
var a = /[Jj]ava([Ss]crit)*/g;
'012345javascript 67890'.search(a) // 6
複製代碼
@params: RegExp | string (正則表達式,能夠設置修飾符)
@params: function | string
@return: 返回一個新的字符串,不改變源對象
複製代碼
若是第一個參數是一個string,那麼 replace()
將直接搜索這個字符串而後進行替換(注意這裏指替換第一個搜索到的結果),並不會先轉成 RegExp
進行匹配。 若是第一個參數是一個 RegExp
,那麼 replace()
將會進行正則匹配,將第一次匹配到結果進行替換,這裏正則表達式能夠設置修飾符。g
會進行全局屢次匹配,將全部匹配到的結果進行替換
若是第二個參數是一個 string
,那麼 replace()
將匹配的結果直接用這個字符串替換 若是第二個參數是一個 function
,咱們看看能夠有哪些參數: @params: 完整模式匹配到的結果 @params: 完整模式下的子模式匹配到的結果,看正則表達式中有多少個圓括號,那麼這裏就能夠有多少個這樣的參數,每一個參數表示對應的子模式匹配的結果 @return: 使用 return
的返回值替換匹配到的值
var a = /([Jj]ava([Ss]cript))\sis\s(fun\w*)/g;
var b = 'ffhfhjavascript is functionsdfffsff';
b.replace(a, function(match) {
// 打印的結果就是 javascript is functionsdfffsff 123
console.log(match, 123);
return '@@@@'
});
// ffhfh@@@@
b.replace(a, function(match, v1, v2, v3) {
// 打印的結果就是 javascript is functionsdfffsff 123
console.log(match, 123);
// javascript
console.log(v1);
// script
console.log(v2);
// functionsdfffsff
console.log(v3);
return '@@@@'
});
// ffhfh@@@@
複製代碼
@params: 正則表達式 | string(經過 `RegExp` 的構造函數轉化成正則表達式)
@return: 數組,
複製代碼
若是這個正則表達式沒有設置修飾符 g
,match()
就不會進行全局檢索,只檢索第一個匹配。在這種狀況下:數組的第一個元素就是就是完整模式匹配的結果,其他的元素則是正則表達式中圓括號的子表達式匹配的結果(若是沒有圓括號就不會有這些元素)。index
表示匹配的位置,input
表示目標字符串
var a = /([Jj]ava([Ss]cript))\sis\s(fun\w*)/;
var b = 'ffhfhjavascript is functionsdfffsff';
b.match(a)
// ["javascript is functionsdfffsff", "javascript", "script", "functionsdfffsff", index: 5, input: "ffhfhjavascript is functionsdfffsff", groups: undefined]
複製代碼
若是添加了修飾符g
,那麼返回的數組中就是屢次全局模式匹配的結果
var a = /123(sxx)456(hello)/g;
var b ='123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello';
b.match(a)
// ["123sxx456hello", "123sxx456hello", "123sxx456hello"]
複製代碼
@params: string
@return: 數組 | null
複製代碼
若是匹配到結果就返回一個數組,結果和字符串的 match()
方法的非全局匹配返回的結果同樣。可是有一點和 match()
不同,就是無論式全局仍是非全局都是同樣的結構。若是沒有匹配到結果就返回 null
var a = /123(sxx)456(hello)/g;
var b ='123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello';
a.exec(b);
// [「123sxx456hello", "sxx", "hello", index: 0, input: "123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello", groups: undefined]
複製代碼
@params: string
@return: boolean
複製代碼
這個方法很簡,匹配成功就返回true
,不然就返回false
javascript權威指南