本文4089字,閱讀大約須要12分鐘。javascript
總括: 本文基於Javascript的正則表達式,結合筆者我的的思考來對正則表達式的特性進行講解。前端
事親以敬,美過三牲。java
相信不少人第一次見到正則表達式的第一印象都是懵逼的,對新手而言一個正則表達式就是一串毫無心義的字符串,讓人摸不着頭腦。但正則表達式是個很是有用的特性,不論是Javascript、PHP、Java仍是Python都有正則表達式。儼然正則表達式已經發展成了一門小語言。做爲編程語言的一部分,它不想變量,函數,對象這種概念那麼容易理解。不少人對於正則表達式的理解都是基於簡單的匹配,等到業務中用到徹底靠從網上copy來解決問題。不得不說,隨着各類開源技術社區的發展,靠copy的確能解決業務中絕大多數的問題,但做爲一名有追求的程序員,是絕對不會讓本身僅僅依靠Ctrl C + Ctrl V
來編程的。本文基於Javascript的正則表達式,結合筆者我的的思考和社區內一些優秀正則表達式文章來對正則表達式進行講解。程序員
簡單介紹下,在Javascript中使用正則表達式有兩種方式:正則表達式
RegExp
構造函數;使用構造函數:express
var regexConst = new RegExp('abc');
複製代碼
使用雙斜槓:編程
var regexLiteral = /abc/;
複製代碼
Javascript中的正則表達式對象主要有兩個方法,test
和exec
:數組
test()
方法接受一個參數,這個參數是用來與正則表達式匹配的字符串,以下例子:app
var regex = /hello/;
var str = 'hello world';
var result = regex.test(str);
console.log(result);
// returns true
複製代碼
exec()
方法在一個指定字符串中執行一個搜索匹配。返回一個結果數組或 null
。dom
var regex = /hello/;
var str = 'hello world';
var result = regex.exec(str);
console.log(result);
// returns [ 'hello', index: 0, input: 'hello world', groups: undefined ]
// 匹配失敗會返回null
// 'hello' 待匹配的字符串
// index: 正則表達式開始匹配的位置
// input: 原始字符串
複製代碼
下文都用test()
方法來進行測試。
標誌是用來表示搜索字符串範圍的一個參數,主要有6個標誌:
標誌 | 描述 |
---|---|
g | 全局搜索。 |
i | 不區分大小寫搜索。 |
m | 多行搜索。 |
s | 容許 . 匹配換行符。 |
u | 使用unicode碼的模式進行匹配。 |
y | 執行「粘性」搜索,匹配從目標字符串的當前位置開始,可使用y標誌。 |
雙斜槓語法:
var re = /pattern/flags;
複製代碼
構造函數語法:
var re = new RegExp("pattern", "flags");
複製代碼
看下實例:
var reg1 = /abc/gi;
var reg2 = new RegExp("abc", "gi");
var str = 'ABC';
console.log(reg1.test(str)); // true
console.log(reg2.test(str)); // true
複製代碼
正則表達式是對字符串進行匹配的一種模式。
請記住,正則表達式是對字符串的操做,因此通常具備字符串類型的編程語言都會有正則表達式。
對於字符串而言,是由兩部分構成的:內容和位置。
好比一個字符串:
'hello World';
複製代碼
它的內容就是:
'h', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'
複製代碼
如上字符串中每個獨立的字母就是這個字符串的內容,而位置指的是:
位置所指就是相鄰字符之間的位置,也就是上圖中箭頭的位置。
匹配內容相比匹配位置來講更爲複雜,先看下簡單的匹配方式:
最簡單的匹配方式就是完整的去匹配一個字符串:
var regex = /hello/;
console.log(regex.test('hello world'));
// true
複製代碼
正則表達式中有不少特殊字符用來匹配字符串,解決的就是匹配多少(按位置匹配)和匹配誰(按內容匹配)的問題。咱們先來看下用來決定匹配誰的一些特殊字符:
簡單的匹配內容有以下的特殊字符:
[xyz]
:字符集,用來匹配方括號中的任意一個字符,好比:
var regex = /[bt]ear/;
console.log(regex.test('tear'));
// returns true
console.log(regex.test('bear'));
// return true
console.log(regex.test('fear'));
// return false
複製代碼
**注意:**除了特殊字符^
外,其它全部的特殊字符在字符集(方括號中)都會失去它的特殊含義。
[^xyz]
:這也是個字符集,和上面字符集不一樣的事,它用來匹配全部不在方括號中的字符。好比:
var regex = /[^bt]ear/;
console.log(regex.test('tear'));
// returns false
console.log(regex.test('bear'));
// return false
console.log(regex.test('fear'));
// return true
複製代碼
針對小寫字母,大寫字母和數字這三種很是經常使用的字符,還提供了比較簡便的寫法:
\d
:至關於[0-9]
,匹配數字字符。
\D
:至關於[^0-9]
,匹配非數字的字符。
\w
:至關於[a-zA-Z0–9_]
,匹配數字、小寫字母、大寫字母和下劃線。
\W
:至關於[^A-Za-z0-9_]
,匹配非數字、非小寫字母、非大寫字母和非下劃線。
[a-z]
:假如咱們想匹配全部的字母,一個笨辦法就是將全部的字母都寫到方括號裏,但很明這種實現很不優雅,不易讀並且很容易遺漏字母。這裏有一種更簡單的實現方案,就是指定字符範圍,好比**[a-h]就是匹配字母a到字母h之間全部的字母,除了小寫字母還能夠匹配數字和大寫字母,[0-9]匹配0到9之間的數字,[A-Z]**匹配A到Z之間全部的大寫字母。好比:
var regex = /[a-z0-9A-Z]ear/;
console.log(regex.test('fear'));
// returns true
console.log(regex.test('tear'));
// returns true
console.log(regex.test('1ear'));
// returns true
console.log(regex.test('Tear'));
// returns true
複製代碼
x|y
:匹配x或是y。好比:
var regex = /(green|red) apple/;
console.log(regex.test('green apple'));
// true
console.log(regex.test('red apple'));
// true
console.log(regex.test('blue apple'));
// false
複製代碼
.
: 匹配除換行符以外的任何單個字符,若是標誌中有s
則也會匹配換行符例子:
var regex = /.n/ ;
console.log(regex.test('an'));
// true
console.log(regex.test('no'));
// false
console.log(regex.test('on'));
// true
console.log(regex.test(` n`));
// false
console.log(/.n/s.test(` n`)); // 注意這裏的正則
// true
複製代碼
\
:這個特殊字符是用來轉義的,好比咱們想匹配方括號,就能夠用\
轉義,一樣相匹配\也能夠用\
轉義,好比:
var regex = /\[\]/;
console.log(regex.test('[]')); // true
複製代碼
上面的特殊字符都只能匹配某個目標字符串一次,但不少場景下咱們須要匹配目標字符串屢次,好比咱們想匹配無數個a
,上面的特殊字符就沒法知足咱們的需求了,所以匹配內容的特殊字符裏還有一部分是用來解決這個問題的:
{n}
:匹配大括號以前的字符n次。例子:
var regex = /go{2}d/;
console.log(regex.test('good'));
// true
console.log(regex.test('god'));
// false
複製代碼
很好理解,上面的正則至關於/good/
。
{n,}
:匹配大括號以前的字符至少n次。例子:
var regex = /go{2,}d/;
console.log(regex.test('good'));
// true
console.log(regex.test('goood'));
// true
console.log(regex.test('gooood'));
// true
複製代碼
{n,m}
:匹配大括號以前的字符至少n次,至多m次。例子:
var regex = /go{1,2}d/;
console.log(regex.test('god'));
// true
console.log(regex.test('good'));
// true
console.log(regex.test('goood'));
// false
複製代碼
爲了更爲方便的使用,還提供了三個比較經常使用規則更爲方便的寫法:
*
:至關於{0,}
。表示前面的字符至少出現0次,也就是出現任意次。+
:至關於{1,}
。表示前面的字符至少出現1次。?
:至關於{0,1}
。表示前面的字符不出現或是出現1次。使用以上內容匹配普通的字符已經能夠知足需求了,但像換行符、換頁符和回車等特殊的符號以上的特殊字符沒法知足需求,所以正則表達式還提供了專門用來匹配特殊符號的特殊字符:
\s
:匹配一個空白字符,包括空格、製表符、換頁符和換行符。看下例子:
var reg = /\s/;
console.log(reg.test(' ')); // true
複製代碼
\S
:匹配一個非空白字符;
\t
:匹配一個水平製表符 。
\n
:匹配一個換行符。
\f
:匹配一個換頁符。
\r
:匹配一個回車符。
\v
:匹配一個垂直製表符。
\0
:匹配 NULL(U+0000)字符。
[\b]
:匹配一個退格。
\cX
:當X是處於A到Z之間的字符的時候,匹配字符串中的一個控制符。
(x)
: 匹配x並記住x,括號內的內容被稱爲捕獲組。這個括號裏強大的是能夠支持子表達式,就是說能夠在括號裏去寫正則,而後做爲一個總體去匹配。這裏還有一個特殊字符叫\n
,這個n和前面換行符不同,這是個變量指的是數字,用來記錄捕獲組序號的。例子:
console.log(/(foo)(bar)\1\2/.test('foobarfoobar')); // true
console.log(/(\d)([a-z])\1\2/.test('1a1a')); // true
console.log(/(\d)([a-z])\1\2/.test('1a2a')); // false
console.log(/(\d){2}/.test('12')); // true
複製代碼
在正則表達式的替換環節,則要使用像 $1
、$2
、...、$n
這樣的語法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')
。$&
表示整個用於匹配的原字符串。
(?:x)
:匹配 'x' 可是不記住匹配項。被稱爲非捕獲組。這裏的\1不會生效,會把它當作普通字符處理。例子:
var regex = /(?:foo)bar\1/;
console.log(regex.test('foobarfoo'));
// false
console.log(regex.test('foobar'));
// false
console.log(regex.test('foobar\1'));
// true
複製代碼
再次強調,這裏的位置是前面圖裏箭頭的位置。
^
:匹配字符串的開始位置,也就是咱們前面位置圖的第一個箭頭的位置。注意和[^xy]
中的^
區分,兩個含義徹底不一樣,看^
例子:
var regex = /^g/;
console.log(regex.test('good'));
// true
console.log(regex.test('bad'));
// false
console.log(regex.test('tag'));
// false
複製代碼
上面正則的含義即匹配字母g開頭的字符串。
$
:匹配字符串的結束位置,例子:
var regex = /.com$/;
console.log(regex.test('test@testmail.com'));
// true
console.log(regex.test('test@testmail'));
// false
複製代碼
上面正則的含義即匹配以.com爲結尾的字符串
\b
:匹配一個詞的邊界。注意匹配的是一個詞的邊界,這個邊界指的是一個詞不被另一個「字」字符跟隨的位置或者前面跟其餘「字」字符的位置。也就是符合要求的某個位置兩邊不全是正常字符或不全是特殊符號的。看例子:
console.log(/\bm/.test('moon')); // true 匹配「moon」中的‘m’,\b的左邊是空字符串,右邊是'm'
console.log(/oo\b/.test('moon')); // false 並不匹配"moon"中的'oo',由於 \b左邊上oo,右邊是n,全是正常字符
console.log(/oon\b/.test('moon')); // true 匹配"moon"中的'oon',\b左邊是oon,右邊是空字符串
console.log(/n\b/.test('moon ')); // true 匹配"moon"中的'n',\b左邊是n,右邊是空格
console.log(/\bm/.test(' moon')); // true 匹配"moon"中的'm',\b左邊是空字符串 右邊是m
console.log(/\b/.test(' ')); // false 沒法匹配空格,\b左邊是空格或空字符串,右邊是空格或是空字符串,沒法知足不全是正常字符或是不全是正常字符
複製代碼
這個若是很差理解,能夠先看\B
,更好理解一點。
\B
: 匹配一個非單詞邊界,和\b
相反,也就是說匹配的是左右兩邊全是正常字符或全是特殊符號的位置。看例子:
console.log(/\B../.test('moon')); // true 匹配'moon'中的'oo' \B左邊是m,右邊是o
console.log(/\B./.exec(' ')); // true 匹配' '中的' ' \B左邊是空字符串,右邊是空格' '
複製代碼
x(?!y)
:僅僅當'x'後面不跟着'y'時匹配'x',這被稱爲正向否認查找。例子:
var regex = /Red(?!Apple)/;
console.log(regex.test('RedOrange')); // true
複製代碼
(?<!y)x
:僅僅當'x'前面不是'y'時匹配'x',這被稱爲反向否認查找。例子:
var regex = /(?<!Red)Apple/;
console.log(regex.test('GreenApple')); // true
複製代碼
x(?=)y
:匹配'x'僅僅當'x'後面跟着'y'.這種叫作先行斷言。例子:
var regex = /Red(?=Apple)/;
console.log(regex.test('RedApple')); // true
複製代碼
(?<=y)x
:匹配'x'僅僅當'x'前面是'y'.這種叫作後行斷言。例子:
var regex = /(?<=Red)Apple/;
console.log(regex.test('RedApple')); // true
複製代碼
方法 | 描述 |
---|---|
RegExp.prototype.exec | 一個在字符串中執行查找匹配的RegExp方法,它返回一個數組(未匹配到則返回 null)。 |
RegExp.prototype.test | 一個在字符串中測試是否匹配的RegExp方法,它返回 true 或 false。 |
String.prototype.match | 一個在字符串中執行查找匹配的String方法,它返回一個數組,在未匹配到時會返回 null。 |
String.prototype.matchAll | 一個在字符串中執行查找全部匹配的String方法,它返回一個迭代器(iterator)。 |
String.prototype.search | 一個在字符串中測試匹配的String方法,它返回匹配到的位置索引,或者在失敗時返回-1。 |
String.prototype.replace | 一個在字符串中執行查找匹配的String方法,而且使用替換字符串替換掉匹配到的子字符串。 |
String.prototype.split | 一個使用正則表達式或者一個固定字符串分隔一個字符串,並將分隔後的子字符串存儲到數組中的 String 方法。 |
var regex = /^\d{10}$/;
console.log(regex.test('9995484545'));
// true
複製代碼
分析下上面的正則:
^
和$
限制了開頭和結尾;\d
用來匹配數字,它至關於[0-9]
;{10}
匹配了\d
表達式,即\d
重複10次;DD-MM-YYYY
或DD-MM-YY
:var regex = /^(\d{1,2}-){2}\d{2}(\d{2})?$/;
console.log(regex.test('10-01-1990'));
// true
console.log(regex.test('2-01-90'));
// true
console.log(regex.test('10-01-190'));
複製代碼
分析上面的正則:
^
和$
限制了開頭和結尾;\d{1,2}
,表示匹配1位或2位數字;-
來匹配連字符,無特殊含義;()
包裹了一個子表達式,也叫捕獲組;{2}
表示匹配上面的子表達式兩次;\d{2}
匹配兩位數字;(\d{2})?
子表達式中匹配兩位數字,而後匹配子表達式一次或是不匹配;var reg = /(\B[A-Z])/g;
'oneTwoThree'.replace(reg, '_$1').toLowerCase();
複製代碼
分析上面的正則:
\B
避免將首字母大寫的字符也轉換掉;([A-Z])
捕獲組捕獲大寫字母;$n
這樣的語法來表示前面的捕獲;toLowerCase
轉爲小寫字母;正則表達式各類規則很難記,但願本篇文章能夠幫你們更好的去記憶這些特殊字符,也但願你們能寫出牛叉的正則表達式。共勉。最後提供一個練習正則表達式的連接:www.hackerrank.com/domains/reg…和一個工具網站:regex101.com/
以上。
能力有限,水平通常,歡迎勘誤,不勝感激。
訂閱更多文章可關注公衆號「前端進階學習」,回覆「666」,獲取一攬子前端技術書籍