var r = /x/g; var s = '_x_x'; r.lastIndex = 4; r.test(s) // false r.lastIndex // 0 r.test(s)
上面代碼指定從字符串的第五個位置開始搜索,這個位置爲空,因此返回false。同時,lastIndex屬性重置爲0,因此第二次執行r.test(s)會返回true。
var r = /a(b+)a/; var arr = r.exec('_abbba_aba_'); arr // ["abbba", "bbb"] arr.index // 1 arr.input // "_abbba_aba_"
var s = 'abba'; var r = /a/g; s.match(r) // ["a", "a"] r.exec(s) // ["a"]
'aaa'.replace('a', 'b') // "baa" 'aaa'.replace(/a/, 'b') // "baa" 'aaa'.replace(/a/g, 'b') // "bbb"
var str = ' #id div.class '; str.replace(/^\s+|\s+$/g, '') // "#id div.class"
'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1') // "world hello" 'abc'.replace('b', '[$`-$&-$\']') // "a[a-b-c]c"
// 非正則分隔 'a, b,c, d'.split(',') // [ 'a', ' b', 'c', ' d' ] // 正則分隔,去除多餘的空格 'a, b,c, d'.split(/, */) // [ 'a', 'b', 'c', 'd' ] // 指定返回數組的最大成員 'a, b,c, d'.split(/, */, 2) [ 'a', 'b' ]
上面代碼使用正則表達式,去除了子字符串的逗號後面的空格。
// 例一 'aaa*a*'.split(/a*/) // [ '', '*', '*' ] // 例二 'aaa**a*'.split(/a*/) // ["", "*", "*", "*"]
上面代碼的分割規則是0次或屢次的a,因爲正則默認是貪婪匹配,因此例一的第一個分隔符是aaa,第二個分割符是a,將字符串分紅三個部分,包含開始處的空字符串。例二的第一個分隔符是aaa,第二個分隔符是0個a(即空字符),第三個分隔符是a,因此將字符串分紅四個部分。
'aaa*a*'.split(/(a*)/) // [ '', 'aaa', '*', 'a', '*' ]
上面代碼的正則表達式使用了括號,第一個組匹配是aaa,第二個組匹配是a,它們都做爲數組成員返回。
大部分字符在正則表達式中,就是字面的含義,好比/a/
匹配a
,/b/
匹配b
。若是在正則表達式之中,某個字符只表示它字面的含義(就像前面的a
和b
),那麼它們就叫作「字面量字符」(literal characters)。javascript
/dog/.test('old dog') // true
上面代碼中正則表達式的dog
,就是字面量字符,因此/dog/
匹配old dog
,由於它就表示d
、o
、g
三個字母連在一塊兒。html
除了字面量字符之外,還有一部分字符有特殊含義,不表明字面的意思。它們叫作「元字符」(metacharacters),主要有如下幾個。java
(1)點字符(.)正則表達式
點字符(.
)匹配除回車(\r
)、換行(\n
) 、行分隔符(\u2028
)和段分隔符(\u2029
)之外的全部字符。注意,對於碼點大於0xFFFF
字符,點字符不能正確匹配,會認爲這是兩個字符。數組
/c.t/
上面代碼中,c.t
匹配c
和t
之間包含任意一個字符的狀況,只要這三個字符在同一行,好比cat
、c2t
、c-t
等等,可是不匹配coot
。網絡
(2)位置字符app
位置字符用來提示字符所處的位置,主要有兩個字符。函數
^
表示字符串的開始位置$
表示字符串的結束位置// test必須出如今開始位置 /^test/.test('test123') // true // test必須出如今結束位置 /test$/.test('new test') // true // 從開始位置到結束位置只有test /^test$/.test('test') // true /^test$/.test('test test') // false
(3)選擇符(|
)ui
豎線符號(|
)在正則表達式中表示「或關係」(OR),即cat|dog
表示匹配cat
或dog
。google
/11|22/.test('911') // true
上面代碼中,正則表達式指定必須匹配11
或22
。
多個選擇符能夠聯合使用。
// 匹配fred、barney、betty之中的一個 /fred|barney|betty/
選擇符會包括它先後的多個字符,好比/ab|cd/
指的是匹配ab
或者cd
,而不是指匹配b
或者c
。若是想修改這個行爲,可使用圓括號。
/a( |\t)b/.test('a\tb') // true
上面代碼指的是,a
和b
之間有一個空格或者一個製表符。
其餘的元字符還包括\
、\*
、+
、?
、()
、[]
、{}
等,將在下文解釋。
正則表達式中那些有特殊含義的元字符,若是要匹配它們自己,就須要在它們前面要加上反斜槓。好比要匹配+
,就要寫成\+
。
/1+1/.test('1+1') // false /1\+1/.test('1+1') // true
上面代碼中,第一個正則表達式之因此不匹配,由於加號是元字符,不表明自身。第二個正則表達式使用反斜槓對加號轉義,就能匹配成功。
正則表達式中,須要反斜槓轉義的,一共有12個字符:^
、.
、[
、$
、(
、)
、|
、*
、+
、?
、{
和\
。須要特別注意的是,若是使用RegExp
方法生成正則對象,轉義須要使用兩個斜槓,由於字符串內部會先轉義一次。
(new RegExp('1\+1')).test('1+1') // false (new RegExp('1\\+1')).test('1+1') // true
上面代碼中,RegExp
做爲構造函數,參數是一個字符串。可是,在字符串內部,反斜槓也是轉義字符,因此它會先被反斜槓轉義一次,而後再被正則表達式轉義一次,所以須要兩個反斜槓轉義。
正則表達式對一些不能打印的特殊字符,提供了表達方法。
\cX
表示Ctrl-[X]
,其中的X
是A-Z之中任一個英文字母,用來匹配控制字符。[\b]
匹配退格鍵(U+0008),不要與\b
混淆。\n
匹配換行鍵。\r
匹配回車鍵。\t
匹配製表符 tab(U+0009)。\v
匹配垂直製表符(U+000B)。\f
匹配換頁符(U+000C)。\0
匹配null
字符(U+0000)。\xhh
匹配一個以兩位十六進制數(\x00
-\xFF
)表示的字符。\uhhhh
匹配一個以四位十六進制數(\u0000
-\uFFFF
)表示的 Unicode 字符。字符類(class)表示有一系列字符可供選擇,只要匹配其中一個就能夠了。全部可供選擇的字符都放在方括號內,好比[xyz]
表示x
、y
、z
之中任選一個匹配。
/[abc]/.test('hello world') // false /[abc]/.test('apple') // true
上面代碼中,字符串hello world
不包含a
、b
、c
這三個字母中的任一個,因此返回false
;字符串apple
包含字母a
,因此返回true
。
有兩個字符在字符類中有特殊含義。
(1)脫字符(^)
若是方括號內的第一個字符是[^]
,則表示除了字符類之中的字符,其餘字符均可以匹配。好比,[^xyz]
表示除了x
、y
、z
以外均可以匹配。
/[^abc]/.test('hello world') // true /[^abc]/.test('bbc') // false
上面代碼中,字符串hello world
不包含字母a
、b
、c
中的任一個,因此返回true
;字符串bbc
不包含a
、b
、c
之外的字母,因此返回false
。
若是方括號內沒有其餘字符,即只有[^]
,就表示匹配一切字符,其中包括換行符。相比之下,點號做爲元字符(.
)是不包括換行符的。
var s = 'Please yes\nmake my day!'; s.match(/yes.*day/) // null s.match(/yes[^]*day/) // [ 'yes\nmake my day']
上面代碼中,字符串s
含有一個換行符,點號不包括換行符,因此第一個正則表達式匹配失敗;第二個正則表達式[^]
包含一切字符,因此匹配成功。
注意,脫字符只有在字符類的第一個位置纔有特殊含義,不然就是字面含義。
(2)連字符(-)
某些狀況下,對於連續序列的字符,連字符(-
)用來提供簡寫形式,表示字符的連續範圍。好比,[abc]
能夠寫成[a-c]
,[0123456789]
能夠寫成[0-9]
,同理[A-Z]
表示26個大寫字母。
/a-z/.test('b') // false /[a-z]/.test('b') // true
上面代碼中,當連字號(dash)不出如今方括號之中,就不具有簡寫的做用,只表明字面的含義,因此不匹配字符b
。只有當連字號用在方括號之中,才表示連續的字符序列。
如下都是合法的字符類簡寫形式。
[0-9.,] [0-9a-fA-F] [a-zA-Z0-9-] [1-31]
上面代碼中最後一個字符類[1-31]
,不表明1
到31
,只表明1
到3
。
連字符還能夠用來指定 Unicode 字符的範圍。
var str = "\u0130\u0131\u0132"; /[\u0128-\uFFFF]/.test(str) // true
上面代碼中,\u0128-\uFFFF
表示匹配碼點在0128
到FFFF
之間的全部字符。
另外,不要過度使用連字符,設定一個很大的範圍,不然極可能選中意料以外的字符。最典型的例子就是[A-z]
,表面上它是選中從大寫的A
到小寫的z
之間52個字母,可是因爲在 ASCII 編碼之中,大寫字母與小寫字母之間還有其餘字符,結果就會出現意料以外的結果。
/[A-z]/.test('\\') // true
上面代碼中,因爲反斜槓('\')的ASCII碼在大寫字母與小寫字母之間,結果會被選中。
預約義模式指的是某些常見模式的簡寫方式。
\d
匹配0-9之間的任一數字,至關於[0-9]
。\D
匹配全部0-9之外的字符,至關於[^0-9]
。\w
匹配任意的字母、數字和下劃線,至關於[A-Za-z0-9_]
。\W
除全部字母、數字和下劃線之外的字符,至關於[^A-Za-z0-9_]
。\s
匹配空格(包括換行符、製表符、空格符等),相等於[ \t\r\n\v\f]
。\S
匹配非空格的字符,至關於[^ \t\r\n\v\f]
。\b
匹配詞的邊界。\B
匹配非詞邊界,即在詞的內部。下面是一些例子。
// \s 的例子 /\s\w*/.exec('hello world') // [" world"] // \b 的例子 /\bworld/.test('hello world') // true /\bworld/.test('hello-world') // true /\bworld/.test('helloworld') // false // \B 的例子 /\Bworld/.test('hello-world') // false /\Bworld/.test('helloworld') // true
上面代碼中,\s
表示空格,因此匹配結果會包括空格。\b
表示詞的邊界,因此world
的詞首必須獨立(詞尾是否獨立未指定),纔會匹配。同理,\B
表示非詞的邊界,只有world
的詞首不獨立,纔會匹配。
一般,正則表達式遇到換行符(\n
)就會中止匹配。
var html = "<b>Hello</b>\n<i>world!</i>"; /.*/.exec(html)[0] // "<b>Hello</b>"
上面代碼中,字符串html
包含一個換行符,結果點字符(.
)不匹配換行符,致使匹配結果可能不符合原意。這時使用\s
字符類,就能包括換行符。
var html = "<b>Hello</b>\n<i>world!</i>"; /[\S\s]*/.exec(html)[0] // "<b>Hello</b>\n<i>world!</i>"
上面代碼中,[\S\s]
指代一切字符。
模式的精確匹配次數,使用大括號({}
)表示。{n}
表示剛好重複n
次,{n,}
表示至少重複n
次,{n,m}
表示重複很多於n
次,很少於m
次。
/lo{2}k/.test('look') // true /lo{2,5}k/.test('looook') // true
上面代碼中,第一個模式指定o
連續出現2次,第二個模式指定o
連續出現2次到5次之間。
量詞符用來設定某個模式出現的次數。
?
問號表示某個模式出現0次或1次,等同於{0, 1}
。*
星號表示某個模式出現0次或屢次,等同於{0,}
。+
加號表示某個模式出現1次或屢次,等同於{1,}
。// t 出現0次或1次 /t?est/.test('test') // true /t?est/.test('est') // true // t 出現1次或屢次 /t+est/.test('test') // true /t+est/.test('ttest') // true /t+est/.test('est') // false // t 出現0次或屢次 /t*est/.test('test') // true /t*est/.test('ttest') // true /t*est/.test('tttest') // true /t*est/.test('est') // true
上一小節的三個量詞符,默認狀況下都是最大可能匹配,即匹配直到下一個字符不知足匹配規則爲止。這被稱爲貪婪模式。
var s = 'aaa'; s.match(/a+/) // ["aaa"]
上面代碼中,模式是/a+/
,表示匹配1個a
或多個a
,那麼到底會匹配幾個a
呢?由於默認是貪婪模式,會一直匹配到字符a
不出現爲止,因此匹配結果是3個a
。
若是想將貪婪模式改成非貪婪模式,能夠在量詞符後面加一個問號。
var s = 'aaa'; s.match(/a+?/) // ["a"]
上面代碼中,模式結尾添加了一個問號/a+?/
,這時就改成非貪婪模式,一旦條件知足,就再也不往下匹配。
除了非貪婪模式的加號,還有非貪婪模式的星號(*
)和非貪婪模式的問號(?
)。
+?
:表示某個模式出現1次或屢次,匹配時採用非貪婪模式。*?
:表示某個模式出現0次或屢次,匹配時採用非貪婪模式。??
:表格某個模式出現0次或1次,匹配時採用非貪婪模式。'abb'.match(/ab*b/) // ["abb"] 'abb'.match(/ab*?b/) // ["ab"] 'abb'.match(/ab?b/) // ["abb"] 'abb'.match(/ab??b/) // ["ab"]
修飾符(modifier)表示模式的附加規則,放在正則模式的最尾部。
修飾符能夠單個使用,也能夠多個一塊兒使用。
// 單個修飾符 var regex = /test/i; // 多個修飾符 var regex = /test/ig;
(1)g 修飾符
默認狀況下,第一次匹配成功後,正則對象就中止向下匹配了。g
修飾符表示全局匹配(global),加上它之後,正則對象將匹配所有符合條件的結果,主要用於搜索和替換。
var regex = /b/; var str = 'abba'; regex.test(str); // true regex.test(str); // true regex.test(str); // true
上面代碼中,正則模式不含g
修飾符,每次都是從字符串頭部開始匹配。因此,連續作了三次匹配,都返回true
。
var regex = /b/g; var str = 'abba'; regex.test(str); // true regex.test(str); // true regex.test(str); // false
上面代碼中,正則模式含有g
修飾符,每次都是從上一次匹配成功處,開始向後匹配。由於字符串abba
只有兩個b
,因此前兩次匹配結果爲true
,第三次匹配結果爲false
。
(2)i 修飾符
默認狀況下,正則對象區分字母的大小寫,加上i
修飾符之後表示忽略大小寫(ignoreCase)。
/abc/.test('ABC') // false /abc/i.test('ABC') // true
上面代碼表示,加了i
修飾符之後,不考慮大小寫,因此模式abc
匹配字符串ABC
。
(3)m 修飾符
m
修飾符表示多行模式(multiline),會修改^
和$
的行爲。默認狀況下(即不加m
修飾符時),^
和$
匹配字符串的開始處和結尾處,加上m
修飾符之後,^
和$
還會匹配行首和行尾,即^
和$
會識別換行符(\n
)。
/world$/.test('hello world\n') // false /world$/m.test('hello world\n') // true
上面的代碼中,字符串結尾處有一個換行符。若是不加m
修飾符,匹配不成功,由於字符串的結尾不是world
;加上之後,$
能夠匹配行尾。
/^b/m.test('a\nb') // true
上面代碼要求匹配行首的b
,若是不加m
修飾符,就至關於b
只能處在字符串的開始處。加上b
修飾符之後,換行符\n
也會被認爲是一行的開始。
(1)概述
正則表達式的括號表示分組匹配,括號中的模式能夠用來匹配分組的內容。
/fred+/.test('fredd') // true /(fred)+/.test('fredfred') // true
上面代碼中,第一個模式沒有括號,結果+
只表示重複字母d
,第二個模式有括號,結果+
就表示匹配fred
這個詞。
下面是另一個分組捕獲的例子。
var m = 'abcabc'.match(/(.)b(.)/); m // ['abc', 'a', 'c']
上面代碼中,正則表達式/(.)b(.)/
一共使用兩個括號,第一個括號捕獲a
,第二個括號捕獲c
。
注意,使用組匹配時,不宜同時使用g
修飾符,不然match
方法不會捕獲分組的內容。
var m = 'abcabc'.match(/(.)b(.)/g); m // ['abc', 'abc']
上面代碼使用帶g
修飾符的正則表達式,結果match
方法只捕獲了匹配整個表達式的部分。這時必須使用正則表達式的exec
方法,配合循環,才能讀到每一輪匹配的組捕獲。
var str = 'abcabc'; var reg = /(.)b(.)/g; while (true) { var result = reg.exec(str); if (!result) break; console.log(result); } // ["abc", "a", "c"] // ["abc", "a", "c"]
正則表達式內部,還能夠用\n
引用括號匹配的內容,n
是從1開始的天然數,表示對應順序的括號。
/(.)b(.)\1b\2/.test("abcabc") // true
上面的代碼中,\1
表示第一個括號匹配的內容(即a
),\2
表示第二個括號匹配的內容(即c
)。
下面是另一個例子。
/y(..)(.)\2\1/.test('yabccab') // true
括號還能夠嵌套。
/y((..)\2)\1/.test('yabababab') // true
上面代碼中,\1
指向外層括號,\2
指向內層括號。
組匹配很是有用,下面是一個匹配網頁標籤的例子。
var tagName = /<([^>]+)>[^<]*<\/\1>/; tagName.exec("<b>bold</b>")[1] // 'b'
上面代碼中,圓括號匹配尖括號之中的標籤,而\1
就表示對應的閉合標籤。
上面代碼略加修改,就能捕獲帶有屬性的標籤。
var html = '<b class="hello">Hello</b><i>world</i>'; var tag = /<(\w+)([^>]*)>(.*?)<\/\1>/g; var match = tag.exec(html); match[1] // "b" match[2] // " class="hello"" match[3] // "Hello" match = tag.exec(html); match[1] // "i" match[2] // "" match[3] // "world"
(2)非捕獲組
(?:x)
稱爲非捕獲組(Non-capturing group),表示不返回該組匹配的內容,即匹配的結果中不計入這個括號。
非捕獲組的做用請考慮這樣一個場景,假定須要匹配foo
或者foofoo
,正則表達式就應該寫成/(foo){1, 2}/
,可是這樣會佔用一個組匹配。這時,就可使用非捕獲組,將正則表達式改成/(?:foo){1, 2}/
,它的做用與前一個正則是同樣的,可是不會單獨輸出括號內部的內容。
請看下面的例子。
var m = 'abc'.match(/(?:.)b(.)/); m // ["abc", "c"]
上面代碼中的模式,一共使用了兩個括號。其中第一個括號是非捕獲組,因此最後返回的結果中沒有第一個括號,只有第二個括號匹配的內容。
下面是用來分解網址的正則表達式。
// 正常匹配 var url = /(http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/; url.exec('http://google.com/'); // ["http://google.com/", "http", "google.com", "/"] // 非捕獲組匹配 var url = /(?:http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/; url.exec('http://google.com/'); // ["http://google.com/", "google.com", "/"]
上面的代碼中,前一個正則表達式是正常匹配,第一個括號返回網絡協議;後一個正則表達式是非捕獲匹配,返回結果中不包括網絡協議。
(3)先行斷言
x(?=y)
稱爲先行斷言(Positive look-ahead),x
只有在y
前面才匹配,y
不會被計入返回結果。好比,要匹配後面跟着百分號的數字,能夠寫成/\d+(?=%)/
。
「先行斷言」中,括號裏的部分是不會返回的。
var m = 'abc'.match(/b(?=c)/); m // ["b"]
上面的代碼使用了先行斷言,b
在c
前面因此被匹配,可是括號對應的c
不會被返回。
(4)先行否認斷言
x(?!y)
稱爲先行否認斷言(Negative look-ahead),x
只有不在y
前面才匹配,y
不會被計入返回結果。好比,要匹配後面跟的不是百分號的數字,就要寫成/\d+(?!%)/
。
/\d+(?!\.)/.exec('3.14') // ["14"]
上面代碼中,正則表達式指定,只有不在小數點前面的數字纔會被匹配,所以返回的結果就是14
。
「先行否認斷言」中,括號裏的部分是不會返回的。
var m = 'abd'.match(/b(?!c)/); m // ['b']
上面的代碼使用了先行否認斷言,b
不在c
前面因此被匹配,並且括號對應的d
不會被返回。