JavaScript正則表達式RegExp

正則表達式,也稱規則表達式,常用其來完成對字符串的校驗和過濾。因爲正則表達式的靈活性、邏輯性和功能性都很是強大,並且 能夠利用很簡單的方式完成對複雜字符串的控制,因此不少程序語言都支持正則表達式。在JavaScript中正則表示也很是強大和實用。javascript

基本形式

正則表達式(regular expression)是一種表達文本模式(即字符串結構)的方法,有點像字符串的模板,經常用做按照「給定模式」匹配文本的工具。好比,正則表達式給出一個Email地址的模式,而後用它來肯定一個字符串是否爲Email地址。JavaScript的正則表達式體系是參照Perl 5創建的。html

新建正則表達式有兩種方法。一種是使用字面量,以斜槓表示開始和結束。java

// 字面量形式
var telRegex1 = /^1[3|5|7|8]\d{9}$/;
// 構造函數形式
var telRegex2 = new RegExp('^1[3|5|7|8]\\d{9}$');

以上都是建立了一個內容爲^1[3|5|7|8]\d{9}$的正則表達式,其表示對一個手機號碼的校驗。必須以1開始,第二位爲3/5/7/8,以後爲9位數字。es6

這兩種寫法——字面量和構造函數——在運行時有一個細微的區別。採用字面量的寫法,正則對象在代碼載入時(即編譯時)生成;採用構造函數的方法,正則對象在代碼運行時生成。考慮到書寫的便利和直觀,實際應用中,基本上都採用字面量的寫法。web

有一點須要注意,使用構造函數建立正則表達式時,傳入的參數是字符串形式的,在字符串內部,\自己也是一個轉義符,所以須要再使用一個\來對其進行正則表達式的轉義。上面第二個示例中,\\d才能表明任意數字。正則表達式

關於正則表達式中,各類符號的含義,以及使用方法,請看後面的介紹:express

元字符

一些經常使用的元字符以下:數組

  • . 匹配除換行符以外的任意字符jsp

  • \w 匹配字母或數字或下劃線或漢字函數

  • \s 匹配任意的空白符

  • \d 匹配數字

  • \b 匹配單詞的開始或結束

  • ^ 匹配字符串的開始處

  • $ 匹配字符串的結束處。

  • * 匹配前面的子表達式任意次。

  • ? 匹配前面子表達式0次或一次,等價於{0, 1}

  • + 匹配以前子表達式一次到屢次,等價於{1, }

  • {n} 匹配以前的子表達式n次。

  • {m,n} 匹配以前的子表達式最少m次,最多n次。

  • {n, } 匹配以前的子表達式至少n次。

  • [xyz] 字符集合,表示其中任意一個字符。表示範圍可用-連接,例如[a-z] 表示a-z之間的任意一個字母。還能夠這樣書寫[A-Za-z0-9]

  • [^xyz] 字符便可,表示非其中任意一個字符。表示範圍可用-連接,例如[^a-z] 表示非 a-z之間的任意一個字母。

  • | 表示或(or)關係,例如 com|cn,表示匹配com或者cn。

  • () 用於分組,其分組中的內容可已經過$1-$9按順序獲取(字符串相關方法中),以後的正則中也能夠經過\1-\9進行引用(正則表達式內部)。(分組0表示整個正則匹配內容或整個正則表達式)

在正則表達式中,以上這些以及一些未列出的元字符都是有自身含義的,若是咱們須要匹配這些元字符自己,可使用\對其進行轉義便可。

更多元字符能夠查看:正則表達式

屬性

修飾符

  • ignoreCase:返回一個布爾值,表示是否設置了i修飾符,該屬性只讀。

  • global:返回一個布爾值,表示是否設置了g修飾符,該屬性只讀。

  • multiline:返回一個布爾值,表示是否設置了m修飾符,該屬性只讀。

  • stickyES6返回一個布爾值,表示是否設置了y修飾符,只讀。

var r = /abc/igm;

r.ignoreCase; // true
r.global;  // true
r.multiline;  // true

匹配時屬性

  • lastIndex:返回下一次開始搜索的位置。該屬性可讀寫,可是隻在設置了g修飾符時有意義。

  • sourceES5返回正則表達式的字符串形式(不包括反斜槓),該屬性只讀。

  • flagsES6返回正則表達式中的修飾符。

var r = /abc/igm;

r.lastIndex; // 0
r.source; // "abc"
r.flags; //"igm"

方法

test()

正則對象的test對象接收一個字符串,表示測試字符串,返回一個布爾值,表示是此字符串是否知足匹配條件。

telRegex1.test('13612341234'); // true
telRegex2.test('13612341234'); // true
telRegex1.test('136123412'); // false

若是正則表達式帶有g修飾符,則每一次test方法都從上一次結束的位置開始向後匹配。同時,能夠經過正則對象的lastIndex屬性指定開始搜索的位置。

var xReg = /x/g;
var str = 'xyz_x1_y1_x3';

xReg.lastIndex; // 0
xReg.test(str); // true

xReg.lastIndex; // 1
xReg.test(str); // true
xReg.lastIndex; // 5

// 指定位置開始 指定下次匹配從最後一位開始,就匹配不到了
xReg.lastIndex = 11; // 11
xReg.test(str); // false

xReg.lastIndex; // 0
var indexReg = /^(?:http|https).+\/jwebui\/pages\/themes\/(\w+)\/\1\.jspx(\?\S+)?$/i ;

上面是一個F8中檢查是否爲首頁的正則表達式。

  • 最開始的^ 和最後的$分別表示匹配的開始和結束。

  • (?:http|https)表示二者之一,這麼寫是非獲取的組匹配,()不會被分組存儲。也能夠寫成(http|https) 可是後面的\1就須要替換成\2了,由於這麼寫時此處造成了第一個分組。

  • .+ 就是任意字符至少出現一次。

  • \/jwebui\/pages\/themes\/ 就是匹配字符串"/jwebui/pages/themes/"

  • (\w+) 做爲第一個分組,表示任意字母或數字或下劃線或漢字至少出現一次。

  • \1表示對第一個分組的引用,再重複第一分組的內容 。

  • \.jspx 表示.jspx

  • (\?\S+)? 表示(\?\S+) 匹配的內容出現0次或一次。其中:

    • \? 表示

    • \S+ 表示任意可見字符出現至少一次。
      `

exec()

正則對象的exec方法,能夠返回匹配結果。若是發現匹配,就返回一個數組,成員是每個匹配成功的子字符串,不然返回null

若是正則表示式包含圓括號(即含有「組匹配」),則返回的數組會包括多個成員。第一個成員是整個匹配成功的結果,後面的成員就是圓括號對應的匹配成功的組。也就是說,第二個成員對應第一個括號,第三個成員對應第二個括號,以此類推。整個數組的length屬性等於組匹配的數量再加1。

var ipReg = /(\d{1,3}\.){3}(\d{1,3})/;
var ipStr = 'My ip is "192.168.118.47" , please tell me yours';

ipReg.exec(ipStr); // ["192.168.118.47", "118.", "47"]

上面第一段代碼表示一個簡單的IP檢驗,數字的1-3位以後緊跟一個.,接着這個總體要出現3次,最後再有一段數字的1-3位。結果數組中,第一個值表示匹配到的結果,以後的表示正則分組匹配到的內容。

若是正則表達式加上g修飾符,則可使用屢次exec方法,下一次搜索的位置從上一次匹配成功結束的位置開始。同時還能夠指定lastIndex,使之下次從指定位置開始(可見以前的test示例)。

var ipLastReg = /\d+(?=;)/g;

var ipsStr = '192.168.118.47;192.168.118.46;192.168.118.48;';

ipLastReg.exec(ipsStr); // ["47"]
ipLastReg.exec(ipsStr); // ["46"]
ipLastReg.exec(ipsStr); // ["48"]

上面代碼中正則中的(?=;)表示先行斷言,表示只匹配在;前面\d+

若是隻是爲了獲得是否匹配,請使用 RegExp.test()方法或字符串實例的.search() 替代,效率更高。

字符串相關方法

之因此稱之爲字符串相關方法是由於其是在字符串上調用的(雖然ES6開始,內部調用的是正則上的方法,但仍是在字符串上提供的入口)。

  • match():返回一個數組,成員是全部匹配的子字符串。

  • search():按照給定的正則表達式進行搜索,返回一個整數,表示匹配開始的位置。

  • replace():按照給定的正則表達式進行替換,返回替換後的字符串。

  • split():按照給定規則進行字符串分割,返回一個數組,包含分割後的各個成員。

match()

match方法對字符串進行正則匹配,返回匹配結果。此方法方法與正則對象的exec方法很是相似:匹配成功返回一個數組,匹配失敗返回null。若是正則表達式帶有g修飾符,則該方法與正則對象的exec方法行爲不一樣,會一次性返回全部匹配成功的結果。

var ipLastReg = /\d+(?=;)/g;
var ipsStr = '192.168.118.47;192.168.118.46;192.168.118.48;';

ipsStr.match(ipLastReg); // ["47", "46", "48"]

上面的正則是匹配IP中的最後一位,其中使用了(?=;)意爲先行斷言,表示只匹配在;以前的內容,可是不包括;。關於更多先行斷言,請看下文。

search()

search方法,返回第一個知足條件的匹配結果(可直接使用字符串,不必定是正則對象)在整個字符串中的位置。若是沒有任何匹配,則返回-1

var nowDateStr = '2016-11-1';
var testReg = /-/g;

nowDateStr.search(testReg); // 4
// 再次查找仍是4
nowDateStr.search(testReg); // 4

//  檢查lastIndex 並設置 
testReg.lastIndex; // 0
testReg.lastIndex = 6;
nowDateStr.search(testReg); // 4  結果仍爲4

search方法老是從字符串的開始位置查找,與正則表達式的g修飾符和lastIndex屬性無關。

replace()

replace方法能夠替換匹配的值,返回替換後的新字符串。它接受兩個參數,第一個是搜索模式(可直接使用字符串,不必定是正則對象),第二個是替換的內容(可以使用字符串或一個函數)。搜索模式若是不加g修飾符,就替換第一個匹配成功的值,不然替換全部匹配成功的值。

其中replace方法的第二個參數可使用美圓符號$,用來指代所替換的內容,具體以下所示:

  • $& 指代匹配的子字符串。

  • $` 指代匹配結果前面的文本。

  • $' 指代匹配結果後面的文本。

  • $n 指代匹配成功的第n組內容,n是從1開始的天然數。

  • $$ 指代美圓符號$。

var re = /-/g; 
var str = '2016-11-01';
var newstr = str.replace(re,'.');
console.log(newstr);  // "2016.11.01"

'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1');
// "world hello"

'abc'.replace('b', '[$`-$&-$\']');
// "a[a-b-c]c"

第二個參數爲函數:

function toCamelStyle(str) {
    // 匹配-以及以後的一個字符,其中這個字符在一個分組內
    var camelRegExp = /-([a-z])/ig;

    return str.replace(camelRegExp, function(all, letter) {
        // all爲匹配到的內容,letter爲組匹配        
        return letter.toUpperCase();
    });
}

toCamelStyle('margin-left'); // "marginLeft"
toCamelStyle('aa-bb-cccc'); // "aaBbCccc"

以上代碼展現經過正則將aa-bb-cccc這樣的字符串轉化爲aaBbCccc 這種形式。replace回調函數接收兩個參數,第一個爲匹配到的內容,第二個爲匹配到的分組,有多少組就能夠傳多少個參數,在此以後還能夠有兩個參數,一個爲匹配到內容在原字符串的位置,另外一個是原字符串。

split()

split方法按照正則規則分割字符串,返回一個由分割後的各個部分組成的數組。該方法接受兩個參數,第一個參數是分隔規則(可直接使用字符串,不必定是正則對象),第二個參數是返回數組的最大成員數。

'2016-11-01'.split('-'); // ["2016", "11", "01"]
'2016-11-01'.split(/-/); // ["2016", "11", "01"]

貪婪模式和懶惰模式

當正則表達式中包含能接受重複的限定符時,一般的行爲是(在使整個表達式能獲得匹配的前提下)匹配儘量多的字符,稱之爲貪婪模式

例如:

var s = 'aaa';
s.match(/a+/); // ["aaa"]

有時,咱們更須要懶惰匹配,也就是匹配儘量少的字符。前面給出的限定符均可以被轉化爲懶惰匹配模式,只要在它後面加上一個問號?。這樣.*?就意味着匹配任意數量的重複,可是在能使整個匹配成功的前提下使用最少的重複。

var s = 'aaa';
s.match(/a+?/); // ["a"]

如下是一些說明

  • *? 重複任意次,但儘量少重複

  • +? 重複1次或更屢次,但儘量少重複

  • ?? 重複0次或1次,但儘量少重複

  • {n,m}? 重複n到m次,但儘量少重複

  • {n,}? 重複n次以上,但儘量少重複

也就是說默認狀況下,都是貪婪模式,加上一個時就轉化爲了懶惰模式,也稱非貪婪模式。

組匹配

一般一個()中的內容就構成了一個分組,此分組內容將被存儲,可在以後的正則表達式(使用\1-\9)和相關方法中(使用 $1-$9)引用,前面已經介紹過了,就再也不說了。

關於組匹配,還有如下幾種狀況:

非捕獲組

(?:x) 稱爲非捕獲組(Non-capturing group),表示不返回該組匹配的內容,即匹配的結果中不計入這個括號。

// 正常匹配
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", "/"]

以後先行斷言先行否認斷言也都是非捕獲組

先行斷言

x(?=y)稱爲先行斷言(Positive look-ahead),x只有在y前面才匹配,y不會被計入返回結果。

好比以前匹配ip的例子:

var ipLastReg = /\d+(?=;)/g;
var ipsStr = '192.168.118.47;192.168.118.46;192.168.118.48;';

ipsStr.match(ipLastReg); // ["47", "46", "48"]

上面正則對象中(?=;)就表示只匹配在;以前的內容,可是不包括;

先行否認斷言

x(?!y)稱爲先行否認斷言(Negative look-ahead),x只有不在y前面才匹配,y不會被計入返回結果。

var xreg = /\d+(?!%)/g ;
xreg.exec('100% is 1'); // ["10"]
xreg.exec('100% is 1'); // ["1"]
/\d+?(?!%)/.exec('100% is 1'); // ["1"]

上面代碼表示匹配不在%前的數字,xreg中直接書寫的\d+ 表示貪婪模式,所以第一次匹配到的是10,第二次纔會匹配到後面的1,由於做爲數字10自己也不在%前面,正則不會將100當成一個總體(注意:這裏須要定義一個正則對象來調用,直接以字面量形式的正則調用時,每次調用都是一個新對象,結果始終是10)。

爲了一次匹配到最後的1,咱們在\d+以後加一個?將其轉爲非貪婪模式便可。

爲了一次匹配到前面100中的1,咱們在\d+以後加一個?將其轉爲非貪婪模式便可。

ES6以前,JavaScript中不支持後行斷言否認後行斷言,ES6中添加了對此的支持,請看以後的ES擴展部分。

ES6擴展

構造函數

RegExp構造函數的參數有兩種狀況。

  • 第一種狀況是,參數是字符串,這時第二個參數表示正則表達式的修飾符(flag)。

  • 第二種狀況是,參數是一個正則表示式,此時不能有第二個參數,會返回一個原有正則表達式的拷貝。

ES6 針對第二種狀況,容許傳入第二個參數,用於設置第一個參數正則表達式的修飾符。

var regex = new RegExp(/xyz/, 'i'); // ES6以前 語法錯誤

new RegExp(/abc/ig, 'i'); // ES6中結果爲: /abc/i

字符串的正則方法

字符串對象共有4個方法,可使用正則表達式:match()replace()search()split()

ES6將這4個方法,在語言內部所有調用RegExp的實例方法,從而作到全部與正則相關的方法,全都定義在RegExp對象上。

修飾符

ES6對正則表達式添加了u修飾符,含義爲「Unicode模式」,用來正確處理大於uFFFF的Unicode字符。也就是說,會正確處理四個字節的UTF-16編碼。

ES6還爲正則表達式添加了y修飾符,叫作「粘連」(sticky)修飾符。

y修飾符的做用與g修飾符相似,也是全局匹配,後一次匹配都從上一次匹配成功的下一個位置開始。不一樣之處在於,g修飾符只要剩餘位置中存在匹配就可,而y修飾符確保匹配必須從剩餘的第一個位置開始,這也就是「粘連」的涵義。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;

// 第一次都能正確匹配
r1.exec(s); // ["aaa"]
r2.exec(s); // ["aaa"]

// 第二次結果就不一致了
r1.exec(s); // ["aa"]
r2.exec(s); // null

我的理解,\y是相似於在每次匹配時隱式地添加了^,表示開始位置。

屬性

ES5中,正則對象存在source屬性,用於返回正則表達式自己。

ES6中,又添加了flags屬性,用於返回正則對象的全部修飾符。

後行斷言

後行斷言於先行斷言相反。例如/(?<=y)x/ 表示匹配x,可是要求x必須在y後面。

同理 後行否認斷言則爲:/(?<!=y)x/ 表示匹配x,可是要求x不能在y後面。

須要注意的是,存在後行斷言時,正則執行順序發生了改變,會先匹配後行斷言的這部分,再匹配其餘的的,順序變成了從右向左。所以一些匹配操做的結果可能大不一致,並且正則中的\1-\9的引用順序也會發生變化。

參考連接

原文發表在個人博客JavaScript正則表達式RegExp,歡迎訪問!

錯誤修正

先行否認斷言中

爲了一次匹配到最後的1,咱們在\d+以後加一個?將其轉爲非貪婪模式便可。

爲了一次匹配到前面100中的1,咱們在\d+以後加一個?將其轉爲非貪婪模式便可。

相關文章
相關標籤/搜索