js中的正則表達(dá)式入門

什么是正則表達(dá)式呢?

正則表達(dá)式(regular expression)描述了一種字符串匹配的模式,可以用來檢查一個字符串是否含有某種子串叼丑、將匹配的子串做替換或者從某個字符串中取出符合某個條件的子串等关翎。

說白了正則表達(dá)式就是處理字符串的,我們可以用它來處理一些復(fù)雜的字符串

為什么要學(xué)習(xí)正則表達(dá)式

我們直接用一個例子來說明

//找出這個字符串中的所有數(shù)字
var str = 'abc123de45fgh6789qqq111';
//方法1
     function findNum(str) {
        var tmp = '',
            arr = [];
        for (var i = 0; i < str.length; i++) {
            var cur = str[i];
            if (!isNaN(cur)) {
                tmp += cur;
            } else {
                if (tmp) {
                    arr.push(tmp);
                    tmp = '';
                }
            }
        }
        if (tmp) {
            arr.push(tmp)
        }
        return arr;
    }
    console.log(findNum(str))
    //["123", "45", "6789", "111"]
    
//方法2 使用正則表達(dá)式
    var reg = /\d+/g;
    console.log(str.match(reg))
   // ["123", "45", "6789", "111"]

通過比較2種方法我們明顯看出在對字符串進(jìn)行處理時鸠信,使用正則表達(dá)式會簡單許多纵寝,所以雖然正則表達(dá)式看起來像是火星文一樣的一堆亂碼的東西,但我們還是有必要去學(xué)習(xí)它的星立。

正則表達(dá)式的創(chuàng)建方式

  • 字面量創(chuàng)建方式
  • 實(shí)例創(chuàng)建方式
    var reg = /pattern/flags
    // 字面量創(chuàng)建方式
    var reg = new RegExp(pattern,flags);
    //實(shí)例創(chuàng)建方式
    
    pattern:正則表達(dá)式  
    flags:標(biāo)識(修飾符)
    標(biāo)識主要包括:
    1. i 忽略大小寫匹配
    2. m 多行匹配爽茴,即在到達(dá)一行文本末尾時還會繼續(xù)尋常下一行中是否與正則匹配的項
    3. g 全局匹配 模式應(yīng)用于所有字符串,而非在找到第一個匹配項時停止

字面量創(chuàng)建方式和構(gòu)造函數(shù)創(chuàng)建方式的區(qū)別

  1. 字面量創(chuàng)建方式不能進(jìn)行字符串拼接绰垂,實(shí)例創(chuàng)建方式可以
var regParam = 'cm';
var reg1 = new RegExp(regParam+'1');
var reg2 = /regParam/;
console.log(reg1);  //   /cm1/
console.log(reg2);  //  /regParam/
  1. 字面量創(chuàng)建方式特殊含義的字符不需要轉(zhuǎn)義室奏,實(shí)例創(chuàng)建方式需要轉(zhuǎn)義
var reg1 = new RegExp('\d');  //    /d/ 
var reg2 = new RegExp('\\d')  //   /\d/
var reg3 = /\d/;              //  /\d/

元字符

代表特殊含義的元字符

\d : 0-9之間的任意一個數(shù)字  \d只占一個位置
\w : 數(shù)字,字母 劲装,下劃線 0-9 a-z A-Z _
\s : 空格或者空白等
\D : 除了\d
\W : 除了\w
\S : 除了\s
 . : 除了\n之外的任意一個字符
 \ : 轉(zhuǎn)義字符
 | : 或者
() : 分組
\n : 匹配換行符
\b : 匹配邊界 字符串的開頭和結(jié)尾 空格的兩邊都是邊界 => 不占用字符串位數(shù)
 ^ : 限定開始位置 => 本身不占位置
 $ : 限定結(jié)束位置 => 本身不占位置
[a-z] : 任意字母 []中的表示任意一個都可以
[^a-z] : 非字母 []中^代表除了
[abc] : abc三個字母中的任何一個 [^abc]除了這三個字母中的任何一個字符

代表次數(shù)的量詞元字符

* : 0到多個
+ : 1到多個
? : 0次或1次 可有可無
{n} : 正好n次胧沫;
{n,} : n到多次
{n,m} : n次到m次

量詞出現(xiàn)在元字符后面 如\d+,限定出現(xiàn)在前面的元字符的次數(shù)

var str = '1223334444';
var reg = /\d{2}/g;
var res = str.match(reg);
console.log(res)  //["12", "23", "33", "44", "44"]

var str ='  我是空格君  ';
var reg = /^\s+|\s+$/g; //匹配開頭結(jié)尾空格
var res = str.replace(reg,'');
console.log('('+res+')')  //(我是空格君)

正則中的()和[]和重復(fù)子項 //拿出來單獨(dú)說一下

  • 一般[]中的字符沒有特殊含義 如+就表示+
    但是像\w這樣的還是有特殊含義的
var str1 = 'abc';
var str2 = 'dbc';
var str3 = '.bc';
var reg = /[ab.]bc/; //此時的.就表示.
reg.test(str1)  //true
reg.test(str2)  //false
reg.test(str3)  //true
  • []中占业,不會出現(xiàn)兩位數(shù)
[12]表示1或者2 不過[0-9]這樣的表示0到9 [a-z]表示a到z
例如:匹配從18到65年齡段所有的人
var reg = /[18-65]/; // 這樣寫對么
reg.test('50')
 //Uncaught SyntaxError: Invalid regular expression: /[18-65]/: Range out of order in character class
//聰明的你想可能是8-6這里不對琳袄,于是改成[16-85]似乎可以匹配16到85的年齡段的,但實(shí)際上發(fā)現(xiàn)這也是不靠譜的

實(shí)際上我們匹配這個18-65年齡段的正則我們要拆開來匹配
我們拆成3部分來匹配 18-19  20-59 60-65 
reg = /(18|19)|([2-5]\d)|(6[0-5])/;
  • ()的提高優(yōu)先級功能:凡是有|出現(xiàn)的時候纺酸,我們一定要注意是否有必要加上()來提高優(yōu)先級;

  • ()的分組 重復(fù)子項 (兩個放到一起說)

分組:
只要正則中出現(xiàn)了小括號那么就會形成一份分組
只要有分組址否,exec(match)和replace中的結(jié)果就會發(fā)生改變(后邊的正則方法中再說)

分組的引用(重復(fù)子項) :
只要在正則中出現(xiàn)了括號就會形成一個分組餐蔬,我們可以通過\n (n是數(shù)字代表的是第幾個分組)來引用這個分組,第一個小分組我們可以用\1來表示

例如:求出這個字符串'abAAbcBCCccdaACBDDabcccddddaab'中出現(xiàn)最多的字母,并求出出現(xiàn)多少次(忽略大小寫)佑附。
var str = 'abbbbAAbcBCCccdaACBDDabcccddddaab';
    str = str.toLowerCase().split('').sort(function(a,b){return a.localeCompare(b)}).join('');

    var reg = /(\w)\1+/ig;
    var maxStr = '';
    var maxLen = 0;
    str.replace(reg,function($0,$1){
        var regLen = $0.length;
        if(regLen>maxLen){
            maxLen = regLen;
            maxStr = $1;
        }else if(maxLen == regLen){
            maxStr += $1;
        }
    })
    console.log(`出現(xiàn)最多的字母是${maxStr},共出現(xiàn)了${maxLen}次`)
  • 當(dāng)我們加()只是為了提高優(yōu)先級而不想捕獲小分組時樊诺,可以在()中加?:來取消分組的捕獲
var str = 'aaabbb';
var reg = /(a+)(?:b+)/;
var res =reg.exec(str);
console.log(res)
//["aaabbb", "aaa", index: 0, input: "aaabbb"]
//只捕獲第一個小分組的內(nèi)容

正則運(yùn)算符的優(yōu)先級

  1. 正則表達(dá)式從左到右進(jìn)行計算,并遵循優(yōu)先級順序音同,這與算術(shù)表達(dá)式非常類似词爬。
  2. 相同優(yōu)先級的會從左到右進(jìn)行運(yùn)算,不同優(yōu)先級的運(yùn)算先高后低权均。
下面是常見的運(yùn)算符的優(yōu)先級排列
依次從最高到最低說明各種正則表達(dá)式運(yùn)算符的優(yōu)先級順序:

\ : 轉(zhuǎn)義符
(), (?:), (?=), []  => 圓括號和方括號
*, +, ?, {n}, {n,}, {n,m}   => 量詞限定符
^, $, \任何元字符顿膨、任何字符 
|       => 替換,"或"操作

字符具有高于替換運(yùn)算符的優(yōu)先級叽赊,一般用 | 的時候恋沃,為了提高 | 的優(yōu)先級,我們常用()來提高優(yōu)先級
如: 匹配 food或者foot的時候 reg = /foo(t|d)/ 這樣來匹配

正則的特性

  • 貪婪性

所謂的貪婪性就是正則在捕獲時必指,每一次會盡可能多的去捕獲符合條件的內(nèi)容囊咏。
如果我們想盡可能的少的去捕獲符合條件的字符串的話,可以在量詞元字符后加?

  • 懶惰性

懶惰性則是正則在成功捕獲一次后不管后邊的字符串有沒有符合條件的都不再捕獲。
如果想捕獲目標(biāo)中所有符合條件的字符串的話梅割,我們可以用標(biāo)識符g來標(biāo)明是全局捕獲

var str = '123aaa456';
var reg = /\d+/;  //只捕獲一次,一次盡可能多的捕獲
var res = str.match(reg)
console.log(res)
// ["123", index: 0, input: "123aaa456"]
reg = /\d+?/g; //解決貪婪性霜第、懶惰性
res = str.match(reg)
console.log(res)
// ["1", "2", "3", "4", "5", "6"]

和正則相關(guān)的一些方法

這里我們只介紹test、exec户辞、match和replace這四個方法

  • reg.test(str) 用來驗(yàn)證字符串是否符合正則 符合返回true 否則返回false
var str = 'abc';
var reg = /\w+/;
console.log(reg.test(str));  //true
  • reg.exec() 用來捕獲符合規(guī)則的字符串
var str = 'abc123cba456aaa789';
var reg = /\d+/;
console.log(reg.exec(str))
//  ["123", index: 3, input: "abc123cba456aaa789"];
console.log(reg.lastIndex)
// lastIndex : 0 

reg.exec捕獲的數(shù)組中 
// [0:"123",index:3,input:"abc123cba456aaa789"]
0:"123" 表示我們捕獲到的字符串
index:3 表示捕獲開始位置的索引
input 表示原有的字符串

當(dāng)我們用exec進(jìn)行捕獲時泌类,如果正則沒有加'g'標(biāo)識符,則exec捕獲的每次都是同一個咆课,當(dāng)正則中有'g'標(biāo)識符時 捕獲的結(jié)果就不一樣了,我們還是來看剛剛的例子

var str = 'abc123cba456aaa789';
var reg = /\d+/g;  //此時加了標(biāo)識符g
console.log(reg.lastIndex)
// lastIndex : 0 

console.log(reg.exec(str))
//  ["123", index: 3, input: "abc123cba456aaa789"]
console.log(reg.lastIndex)
// lastIndex : 6

console.log(reg.exec(str))
// ["456", index: 9, input: "abc123cba456aaa789"]
console.log(reg.lastIndex)
// lastIndex : 12

console.log(reg.exec(str))
// ["789", index: 15, input: "abc123cba456aaa789"]
console.log(reg.lastIndex)
// lastIndex : 18

console.log(reg.exec(str))
// null
console.log(reg.lastIndex)
// lastIndex : 0

每次調(diào)用exec方法時,捕獲到的字符串都不相同
lastIndex :這個屬性記錄的就是下一次捕獲從哪個索引開始末誓。
當(dāng)未開始捕獲時,這個值為0书蚪。          
如果當(dāng)前次捕獲結(jié)果為null喇澡。那么lastIndex的值會被修改為0.下次從頭開始捕獲。
而且這個lastIndex屬性還支持人為賦值殊校。

exec的捕獲還受分組()的影響

var str = '2017-01-05';
var reg = /-(\d+)/g
// ["-01", "01", index: 4, input: "2017-01-05"]
"-01" : 正則捕獲到的內(nèi)容
"01"  : 捕獲到的字符串中的小分組中的內(nèi)容
  • str.match(reg) 如果匹配成功晴玖,就返回匹配成功的數(shù)組,如果匹配不成功为流,就返回null
//match和exec的用法差不多
var str = 'abc123cba456aaa789';
var reg = /\d+/;
console.log(reg.exec(str));
//["123", index: 3, input: "abc123cba456aaa789"]
console.log(str.match(reg));
//["123", index: 3, input: "abc123cba456aaa789"]

上邊兩個方法console的結(jié)果有什么不同呢呕屎?二個字符串是一樣滴。
當(dāng)我們進(jìn)行全局匹配時敬察,二者的不同就會顯現(xiàn)出來了.

var str = 'abc123cba456aaa789';
var reg = /\d+/g;
console.log(reg.exec(str));
// ["123", index: 3, input: "abc123cba456aaa789"]
console.log(str.match(reg));
// ["123", "456", "789"]

當(dāng)全局匹配時秀睛,match方法會一次性把符合匹配條件的字符串全部捕獲到數(shù)組中,
如果想用exec來達(dá)到同樣的效果需要執(zhí)行多次exec方法。

我們可以嘗試著用exec來簡單模擬下match方法的實(shí)現(xiàn)莲祸。

 String.prototype.myMatch = function (reg) {
    var arr = [];
    var res = reg.exec(this);
    if (reg.global) {
        while (res) {
            arr.push(res[0]);
            res = reg.exec(this)
        }
    }else{
        arr.push(res[0]);
    }
    return arr;
}

var str = 'abc123cba456aaa789';
var reg = /\d+/;
console.log(str.myMatch(reg))
// ["123"]

var str = 'abc123cba456aaa789';
var reg = /\d+/g;
console.log(str.myMatch(reg))
// ["123", "456", "789"]

此外蹂安,match和exec都可以受到分組()的影響,不過match只在沒有標(biāo)識符g的情況下才顯示小分組的內(nèi)容锐帜,如果有全局g田盈,則match會一次性全部捕獲放到數(shù)組中

var str = 'abc';
var reg = /(a)(b)(c)/;

console.log( str.match(reg) );
// ["abc", "a", "b", "c", index: 0, input: "abc"]
console.log( reg.exec(str) );
// ["abc", "a", "b", "c", index: 0, input: "abc"]


當(dāng)有全局g的情況下
var str = 'abc';
var reg = /(a)(b)(c)/g;
console.log( str.match(reg) );
// ["abc"]
console.log( reg.exec(str) );
// ["abc", "a", "b", "c", index: 0, input: "abc"]
  • str.replace() 這個方法大家肯定不陌生,現(xiàn)在我們要說的就是和這個方法和正則相關(guān)的東西了缴阎。
正則去匹配字符串允瞧,匹配成功的字符去替換成新的字符串
寫法:str.replace(reg,newStr);

var str = 'a111bc222de';
var res = str.replace(/\d/g,'Q')
console.log(res)
// "aQQQbcQQQde"

replace的第二個參數(shù)也可以是一個函數(shù)
str.replace(reg,fn);

var str = '2017-01-06';
str = str.replace(/-\d+/g,function(){
    console.log(arguments)
})

控制臺打印結(jié)果:
["-01", 4, "2017-01-06"]
["-06", 7, "2017-01-06"]
"2017undefinedundefined"
從打印結(jié)果我們發(fā)現(xiàn)每一次輸出的值似乎跟exec捕獲時很相似,既然與exec似乎很相似蛮拔,那么似乎也可以打印出小分組中的內(nèi)容嘍 

var str = '2017-01-06';
str = str.replace(/-(\d+)/g,function(){
    console.log(arguments)
})
["-01", "01", 4, "2017-01-06"]
["-06", "06", 7, "2017-01-06"]
"2017undefinedundefined"
從結(jié)果看來我們的猜測沒問題述暂。

此外,我們需要注意的是语泽,如果我們需要替換replace中正則找到的字符串贸典,函數(shù)中需要一個返回值去替換正則捕獲的內(nèi)容。

通過replace方法獲取url中的參數(shù)的方法

(function(pro){
    function queryString(){
        var obj = {},
            reg = /([^?&#+]+)=([^?&#+]+)/g;
        this.replace(reg,function($0,$1,$2){
            obj[$1] = $2;
        })
        return obj;
    }
    pro.queryString = queryString;
}(String.prototype));

// 例如 url為 https://www.baidu.com?a=1&b=2
// window.location.href.queryString();
// {a:1,b:2}

零寬斷言

用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或之后的東西踱卵,如\b,^,$那樣用于指定一個位置廊驼,這個位置應(yīng)該滿足一定的條件(即斷言)据过,因此它們也被稱為零寬斷言。

在使用正則表達(dá)式時妒挎,捕獲的內(nèi)容前后必須是特定的內(nèi)容绳锅,而我們又不想捕獲這些特定內(nèi)容的時候,零寬斷言就可以派上用場了酝掩。

  • 零寬度正預(yù)測先行斷言 (?=exp)
  • 零寬度負(fù)預(yù)測先行斷言 (?!exp)
  • 零寬度正回顧后發(fā)斷言 (?<=exp)
  • 零寬度負(fù)回顧后發(fā)斷言 (?<!exp)

這四胞胎看著名字好長鳞芙,給人一種好復(fù)雜好難的感覺,我們還是挨個來看看它們究竟是干什么的吧期虾。

  • (?=exp) 這個簡單理解就是說字符出現(xiàn)的位置的右邊必須匹配到exp這個表達(dá)式原朝。
var str = "i'm singing and dancing";
var reg = /\b(\w+(?=ing\b))/g
var res = str.match(reg);
console.log(res)
// ["sing", "danc"]


注意一點(diǎn),這里說到的是位置镶苞,不是字符喳坠。
var str = 'abc';
var reg = /a(?=b)c/;
console.log(res.test(str));  // false

// 這個看起來似乎是正確的,實(shí)際上結(jié)果是false
reg中a(?=b)匹配字符串'abc' 字符串a(chǎn)的右邊是b這個匹配沒問題,接下來reg中a(?=b)后邊的c匹配字符串時是從字符串'abc'中a的后邊b的前邊的這個位置開始匹配的茂蚓,
這個相當(dāng)于/ac/匹配'abc',顯然結(jié)果是false了
  • (?!exp) 這個就是說字符出現(xiàn)的位置的右邊不能是exp這個表達(dá)式壕鹉。
var str = 'nodejs';
var reg = /node(?!js)/;
console.log(reg.test(str)) // false
  • (?<=exp) 這個就是說字符出現(xiàn)的位置的前邊是exp這個表達(dá)式。
var str = '¥998$888';
var reg = /(?<=\$)\d+/;
console.log(reg.exec(str)) //888
  • (?<!exp) 這個就是說字符出現(xiàn)的位置的前邊不能是exp這個表達(dá)式聋涨。
var str = '¥998$888';
var reg = /(?<!\$)\d+/;
console.log(reg.exec(str)) //998

最后晾浴,來一張思維導(dǎo)圖

Paste_Image.png

圖片如果放大也看不清的話 下載地址
如有錯誤歡迎指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牍白,一起剝皮案震驚了整個濱河市脊凰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茂腥,老刑警劉巖笙各,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異础芍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)数尿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門仑性,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人右蹦,你說我怎么就攤上這事诊杆。” “怎么了何陆?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵晨汹,是天一觀的道長。 經(jīng)常有香客問我贷盲,道長淘这,這世上最難降的妖魔是什么剥扣? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮铝穷,結(jié)果婚禮上钠怯,老公的妹妹穿的比我還像新娘。我一直安慰自己曙聂,他們只是感情好晦炊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宁脊,像睡著了一般断国。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榆苞,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天稳衬,我揣著相機(jī)與錄音,去河邊找鬼语稠。 笑死宋彼,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仙畦。 我是一名探鬼主播输涕,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慨畸!你這毒婦竟也來了莱坎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤寸士,失蹤者是張志新(化名)和其女友劉穎檐什,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱卡,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乃正,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了婶博。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓮具。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖凡人,靈堂內(nèi)的尸體忽然破棺而出名党,到底是詐尸還是另有隱情,我是刑警寧澤挠轴,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布传睹,位于F島的核電站,受9級特大地震影響岸晦,放射性物質(zhì)發(fā)生泄漏欧啤。R本人自食惡果不足惜睛藻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望堂油。 院中可真熱鬧修档,春花似錦、人聲如沸府框。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迫靖。三九已至院峡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間系宜,已是汗流浹背照激。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盹牧,地道東北人俩垃。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像汰寓,于是被迫代替她去往敵國和親口柳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容