正则表达式从基础到深入实战
正則表達(dá)式從基礎(chǔ)到深入實(shí)戰(zhàn)
什么是正則?
正則就是一個(gè)規(guī)則,用來(lái)處理**字符串**的規(guī)則 1、正則匹配 編寫(xiě)一個(gè)規(guī)則,驗(yàn)證某個(gè)字符串是否符合這個(gè)規(guī)則,正則匹配使用的是 test 方法
2、正則捕獲 編寫(xiě)一個(gè)規(guī)則,在一個(gè)字符串中把符合規(guī)則的內(nèi)容都獲取到,正則捕獲使用的方法:正則的exec方法、字符串中的split、replace、match等方法都支持正則
var reg = /^$/; //=>兩個(gè)斜杠中間包含一些內(nèi)容就是正則,兩個(gè)斜杠之間包含的全部?jī)?nèi)容都是元字符 復(fù)制代碼正則的元字符和修飾符
任何一個(gè)正則都是由 元字符 和 修飾符 組成的
修飾符 g(global):全局匹配 i(ignoreCase):忽略大小寫(xiě)匹配 m(multiline):多行匹配
元字符 [量詞元字符] +:讓前面的元字符出現(xiàn)一到多次 ?:出現(xiàn)零到一次 *:出現(xiàn)零到多次 {n}:出現(xiàn)n次 {n,}:出現(xiàn)n到多次 {n,m}:出現(xiàn)n到m次
[特殊意義的元字符] \:轉(zhuǎn)義字符(把一個(gè)普通字符轉(zhuǎn)變?yōu)橛刑厥庖饬x的字符,或者把一個(gè)有意義字符轉(zhuǎn)換為普通的字符) .:除了\n(換行符)以外的任意字符 \d:匹配一個(gè)0~9之間的數(shù)字
\D:匹配任意一個(gè)非0~9之間的數(shù)字(大寫(xiě)字母和小寫(xiě)字母的組合正好是反向的) \w:匹配一個(gè) 0~9或字母或_ 之間的字符 \s:匹配一個(gè)任意空白字符 \b:匹配一個(gè)邊界符 x|y:匹配x或者y中的一個(gè) [a-z]:匹配a-z中的任意一個(gè)字符 [^a-z]:和上面的相反,匹配任意一個(gè)非a-z的字符 [xyz]:匹配x或者y或者z中的一個(gè)字符 [^xyz]:匹配除了xyz以外的任意字符 ():正則的小分組,匹配一個(gè)小分組(小分組可以理解為大正則中的一個(gè)小正則) ^:以某一個(gè)元字符開(kāi)始 $:以某一個(gè)元字符結(jié)束 ?::只匹配不捕獲 ?=:正向預(yù)查 ?!:負(fù)向預(yù)查 ......
除了以上特殊元字符和量詞元字符,其余的都叫做普通元字符:代表本身意義的元字符
元字符詳細(xì)解讀
^ $
var reg = /\d+/; //=>包含某某某即可,這里說(shuō)明包含1到多個(gè)數(shù)字即可 var str = '正則匹配2018'; reg.test(str) =>truereg=/^\d+/; reg.test(str) =>falsereg=/^\d+$/;//=>只能是某某某的,這里說(shuō)明只能是1到多個(gè)數(shù)字 reg.test('2017'); =>true reg.test('2017正則2018'); =>false reg.test('2'); =>true ^或者$只是一個(gè)修飾或者聲明,不會(huì)占據(jù)字符串的位置 復(fù)制代碼\
var reg = /^2.3$/; reg.test('2.3'); =>true reg.test('2+3'); =>true 點(diǎn)在正則中的意思:匹配除了\n以外的任意字符,而不是單純的小數(shù)點(diǎn)reg = /^2\.3$/; reg.test('2.3'); =>true reg.test('2+3'); =>false 使用轉(zhuǎn)義字符把點(diǎn)轉(zhuǎn)換為本身小數(shù)點(diǎn)的意思 復(fù)制代碼x|y
var reg = /^18|19$/;//=>18 19 189 119 819 181 1819 ... 很多都符合這個(gè)規(guī)則 /** 18或者19* 以1開(kāi)頭 以9結(jié)尾 中間是8或者1 * 以18開(kāi)頭或者以19結(jié)尾即可 =>'18珠峰' '珠峰19'...*/var reg = /^(18|19)$/;//=>此時(shí)只有18或者19符合我們的規(guī)則了 復(fù)制代碼():正則中的分組,也可以理解為一個(gè)大正則中的一個(gè)正則(包起來(lái)的部分是一個(gè)整體);在正則中我們可以使用小括號(hào)改變一些默認(rèn)的優(yōu)先級(jí);
小分組還有第二個(gè)作用:分組引用 小分組的第三個(gè)作用:分組捕獲
//=>分組引用:\1 或者 \2 ...出現(xiàn)和第N個(gè)分組一模一樣的內(nèi)容 var reg = /^([a-z])([a-z])\2([a-z])$/; //=> 符合的字符串:foot、book、week、attr、http... 復(fù)制代碼[]
[xyz] [^xyz] [a-z] [^a-z]
//=>\w:數(shù)組字母下劃線中的任意一個(gè)字符 var reg = /^[a-zA-Z0-9_]$/; //=>等價(jià)于\w//=>中括號(hào)中出現(xiàn)的元字符,一般都代表本身的含義 var reg = /^[.?+&]+$/; //=>里面的四個(gè)元字符都是本身含義,例如:點(diǎn)就是小數(shù)點(diǎn)了,不是所謂的任意字符...//=>需求:描述樣式類(lèi)名的規(guī)則(數(shù)字、字母、下劃線、-),并且不能以-開(kāi)頭 //var reg = /^[\w-]+$/; //var reg = /^[0-9a-zA-Z_-]+$/; //=>沒(méi)有處理以-開(kāi)頭的情況 var reg = /^\w[\w-]*$/; 復(fù)制代碼//=>需求:驗(yàn)證18~65之間的年齡 //var reg = /^[18-65]$/; //=>1或者8~6或者5中的任意一個(gè)字符,中括號(hào)中出現(xiàn)的18不是數(shù)字18,而是1或者8,當(dāng)前正則是非法的:因?yàn)椴荒茉O(shè)置8~6這種范圍//=>分三個(gè)階段處理: // 18 或者 19 /(18|19)/ // 20 ~ 59 /([2-5]\d)/ // 60 ~ 65 /(6[0-5])/ var reg = /^((18|19)|([2-5]\d)|(6[0-5]))$/; 復(fù)制代碼常用的正則表達(dá)式編寫(xiě)
驗(yàn)證是否為有效數(shù)字
/** 可能是正數(shù),可能是負(fù)數(shù) 12 -12* 整數(shù)或者小數(shù) 0 12 0.2 12.5 -12.3* 只要出現(xiàn)小數(shù)點(diǎn),后面至少要跟一位數(shù)字* 小數(shù)點(diǎn)前面必須有數(shù)字*/ var reg = /^-?(\d|([1-9]\d+))(\.\d+)?$/; /** -? 負(fù)號(hào)可有可無(wú)* (\d|([1-9]\d+))* \d 一位數(shù)可以是任何值* ([1-9]\d+) 多位數(shù)不能以零開(kāi)頭* (\.\d+)? 小數(shù)部分可有可無(wú),有的話點(diǎn)后面必須跟一位數(shù)字*/ 復(fù)制代碼手機(jī)號(hào)碼
/** 11位數(shù)字* 1開(kāi)頭*/ var reg = /^1\d{10}$/; 復(fù)制代碼用戶名:真實(shí)姓名
//=>/^[\u4E00-\u9FA5]$/ 中文漢字的正則 var reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10})?$/; 復(fù)制代碼郵箱
var reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/; /** 以數(shù)字字母下劃線開(kāi)頭* @前面可以是 數(shù)字、字母、下劃線、-、. 這些符號(hào)* 不能把 -和. 連續(xù)出現(xiàn),出現(xiàn)一次后面必須跟數(shù)字字母下劃線* * @后面的部分支持* 企業(yè)郵箱* .com.cn 多域名情況*/ // [A-Za-z0-9]+ // ((\.|-)[A-Za-z0-9]+)* // \.[A-Za-z0-9]+ // @163.com.cn // @chen-wan-jun.com.cn 復(fù)制代碼身份證號(hào)碼
/** 18位* 前17位必須是數(shù)字* 最后一位可以是數(shù)字或者X(X代表數(shù)字10)* * 371426198806064414* 前六位:省市縣 371426* 接下來(lái)八位 出生年+月+日* 倒數(shù)第二位數(shù)字 奇數(shù)代表男 偶數(shù)代表女*/ var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/; //=>這樣寫(xiě)不僅可以匹配,而且以后捕獲的時(shí)候,不僅可以把大正則匹配的結(jié)果捕獲到,里面每一個(gè)小分組(小正則)匹配的結(jié)果也可以單獨(dú)的捕獲到 “分組捕獲”//=>年 1950~2017 //=>第一段 1950~1999 //=>第二段 2000~2017 //==> 00~09 //==> 10~17 // /^((19[5-9]\d)|(20((0\d)|(1[0-7]))))$/ 復(fù)制代碼正則捕獲
把當(dāng)前字符串中符合正則的字符捕獲到 RegExp.prototype:exec 實(shí)現(xiàn)正則捕獲的方法
var str = '正則匹配2017揚(yáng)帆起航2018'; var reg = /\d+/;reg.exec(str); /** 當(dāng)正則捕獲的時(shí)候:* 1、先去驗(yàn)證當(dāng)前字符串和正則是否匹配,如果不匹配返回的結(jié)果是null(沒(méi)有捕獲到任何的內(nèi)容)* 2、如果匹配,從字符串最左邊開(kāi)始,向右查找到匹配的內(nèi)容,并且把匹配的內(nèi)容返回* * exec捕獲到結(jié)果的格式:* -> 獲取的結(jié)果是一個(gè)數(shù)組* -> 數(shù)組中的第一項(xiàng)是當(dāng)前本次大正則在字符串中匹配到的結(jié)果* -> index:記錄了當(dāng)前本次捕獲到結(jié)果的起始索引* -> input:當(dāng)前正則操作的原始字符串* -> 如果當(dāng)前正則中有分組,獲取的數(shù)組中,從第二項(xiàng)開(kāi)始都是每個(gè)小分組,本次匹配到的結(jié)果(通過(guò)exec可以把分組中的內(nèi)容捕獲到)* * 執(zhí)行一次exec只能把符合正則規(guī)則條件中的一個(gè)內(nèi)容捕獲都,如果還有其它符合規(guī)則的,需要在次執(zhí)行exec才有可能捕獲到*/ 復(fù)制代碼正則捕獲存在懶惰性
執(zhí)行一次exec捕獲到第一個(gè)符合規(guī)則的內(nèi)容,第二次執(zhí)行exec,捕獲到的依然是第一個(gè)匹配的內(nèi)容,后面匹配的內(nèi)容不管執(zhí)行多少次exec都無(wú)法捕獲到
解決正則捕獲的懶惰性: 在正則的末尾加修飾符g(全局匹配)
//=>正則為什么會(huì)存在懶惰性? /** 正則本身有一個(gè)屬性:lastIndex(下一次正則在字符串中匹配查找的開(kāi)始索引)* 默認(rèn)值:0,從字符串第一個(gè)字符開(kāi)始查找匹配的內(nèi)容* 默認(rèn)不管指定多少遍exec方法,正則的lastIndex值都不會(huì)變(也就是第二次以后查找的時(shí)候還是從第一個(gè)字符找,所以找到的結(jié)果永遠(yuǎn)都是第一個(gè)匹配的內(nèi)容)* 而且當(dāng)我們手動(dòng)把 lastIndex 進(jìn)行修改的時(shí)候,不會(huì)起到任何的作用 *///=>為什么加修飾符g就解決了懶惰性? /* * 加了修飾符g,每一次exec結(jié)束后,瀏覽器默認(rèn)會(huì)把lastIndex值進(jìn)行修改,下一次從上一次結(jié)束的位置開(kāi)始查找,所以可以得到后面匹配的內(nèi)容了*/var reg = /\d+/g; var str = '正則匹配2017楊帆起航2018'; console.log(reg.lastIndex);//=>0 console.log(reg.exec(str)[0]);//=>'2017'console.log(reg.lastIndex);//=>8 console.log(reg.exec(str)[0]);//=>'2018'console.log(reg.lastIndex);//=>16 console.log(reg.exec(str));//=>nullconsole.log(reg.lastIndex);//=>0 console.log(reg.exec(str)[0]);//=>'2017' 復(fù)制代碼exec有自己的局限性:執(zhí)行一次exec只能捕獲到一個(gè)和正則匹配的結(jié)果(即使加了修飾符g),如果需要都捕獲到,我們需要執(zhí)行N次exec方法才可以
下面封裝的myExecAll方法,目的是執(zhí)行一次這個(gè)方法,可以把當(dāng)前正則匹配到的全部?jī)?nèi)容都捕獲到
RegExp.prototype.myExecAll = function myExecAll() {var str = arguments[0] || '',result = [];//=>首先判斷THIS是否加了全局修飾符G,如果沒(méi)有加,為了防止下面執(zhí)行出現(xiàn)死循環(huán),我們只讓其執(zhí)行一次EXEC即可,把執(zhí)行一次的結(jié)果直接的返回if (!this.global) {return this.exec(str);}var ary = this.exec(str);while (ary) {result.push(ary[0]);ary = this.exec(str);}return result; };var reg = /\d+/g; console.log(reg.myExecAll('正則2017匹配2018楊帆2019起航2020')); 復(fù)制代碼使用字符串方法match實(shí)現(xiàn)捕獲
var reg = /\d+/g; var str = '正則2017匹配2018楊帆2019起航2020'; str.match(reg); //=>["2017", "2018", "2019", "2020"] 復(fù)制代碼使用字符串match捕獲: 1、如果正則加了修飾符g,執(zhí)行一次match會(huì)把所有正則匹配的內(nèi)容捕獲到 2、如果沒(méi)有加修飾符g,執(zhí)行一次match只能把第一個(gè)匹配的結(jié)果捕獲到
局限性: 在加了修飾符g的情況下,執(zhí)行match方法只能把大正則匹配的內(nèi)容捕獲到,對(duì)于小分組捕獲的內(nèi)容方法給其自動(dòng)忽略了
var str = 'my name is {0},i am {1} years old~,2017'; //=>需求:把{n}整體捕獲到,而且還要把括號(hào)中的數(shù)字也獲取到 var reg = /\{(\d+)\}/g;// str.match(reg);//=>["{0}", "{1}"] //=>想要獲取小分組中的內(nèi)容,我們只能使用EXEC處理了 function fn(reg,str){var ary=reg.exec(str),result=[];while(ary){result.push(ary);ary=reg.exec(str);}return result; } 復(fù)制代碼使用test也可以實(shí)現(xiàn)正則的捕獲
不管是正則的匹配還是正則的捕獲,在處理時(shí)候的原理是沒(méi)區(qū)別的:從字符串的第一個(gè)字符向后查找,找到符合正則規(guī)則的字符,如果可以找到,說(shuō)明正則和字符串匹配(test檢測(cè)返回true、exec捕獲返回捕獲的內(nèi)容),如果找到末尾都沒(méi)有匹配的,說(shuō)明正則和字符串不匹配(test檢測(cè)返回false、exec捕獲返回null)
如果正則設(shè)置了修飾符g,不管使用test還是exec中的任何方法,都會(huì)修改lastIndex值(下一次查找是基于上一次匹配結(jié)果的末尾開(kāi)始查找的)
//=>如果當(dāng)前字符串和正則是匹配的,我們進(jìn)行捕獲 var reg = /\{(\d+)\}/g; var str = 'my name is {0}~~'; if (reg.test(str)) {//=>reg.test(str) : trueconsole.log(reg.lastIndex);//=>14console.log(reg.exec(str));//=>null }var reg = /\{(\d+)\}/; var str = 'my name is {0}~~'; if (reg.test(str)) {//=>reg.test(str) : trueconsole.log(reg.lastIndex);//=>0console.log(reg.exec(str));//=>['{0}','0'...] } 復(fù)制代碼使用test不僅可以找到匹配的內(nèi)容,也能像exec一樣把找到的內(nèi)容獲取到 test返回結(jié)果是 true/false,所以靠返回結(jié)果肯定不行
var reg = /\{(\d+)\}/g; var str = 'my name is {0}~~,i am {1} years old~~'; reg.test(str);//=>true console.log(RegExp.$1);//=>0 獲取到當(dāng)前本次匹配內(nèi)容中第一個(gè)小分組捕獲的內(nèi)容reg.test(str);//=>true console.log(RegExp.$1);//=>1 TEST可以實(shí)現(xiàn)捕獲,但是每一次只能獲取到當(dāng)前本次匹配結(jié)果中,第N個(gè)分組捕獲的內(nèi)容 $1第一個(gè)分組 $2第二個(gè)分組 ... 復(fù)制代碼所有支持正則的方法都可以實(shí)現(xiàn)正則的捕獲(一般都是字符串方法)
字符串中常用的支持正則的方法: match split replace ...
var str = 'name=正則&age=8&lx=teacher'; str.split(/&|=/); //=>["name", "正則", "age", "8", "lx", "teacher"]str.split(/(&|=)/); //=>["name", "=", "正則", "&", "age", "=", "8", "&", "lx", "=", "teacher"]//=>在使用split進(jìn)行字符串拆分的時(shí)候,如果正則中包含小分組,會(huì)把小分組中的內(nèi)容都捕獲到,放在最后的數(shù)組中//=>本案例中的小括號(hào)僅僅是為了實(shí)現(xiàn) 改變默認(rèn)的優(yōu)先級(jí) 問(wèn)題,但是我們不想把分組中的內(nèi)容捕獲到 => “只想匹配不想捕獲” 我們可以使用 (?:) str.split(/(?:&|=)/); //=>["name", "珠峰", "age", "8", "lx", "teacher"]//=>只匹配不捕獲: //在當(dāng)前一個(gè)分組中加了 ?: ,在正則檢測(cè)匹配的時(shí)候,小分組可以起到自己應(yīng)有的作用(例如:改變優(yōu)先級(jí)...),但是在捕獲的時(shí)候,遇到帶?:的小分組,瀏覽器不會(huì)把當(dāng)前這個(gè)分組中匹配的內(nèi)容,單獨(dú)去捕獲了var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/; reg.exec(''); //=>["371426198806064414", "371426", "1988", "06", "06", "44", "1", "4"...]var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(?:\d{2})(\d)(?:\d|X)$/; reg.exec('371426198806064414');//=> ["371426198806064414", "371426", "1988", "06", "06", "4"...]var reg = /^-?(\d|([1-9]\d+))(\.\d+)?$/;//=>計(jì)算是第幾個(gè)分組的時(shí)候,我們從左向右找 ( 即可 復(fù)制代碼replace
replace:字符串中原有字符的替換 str.replace(old,new)
var str = '正則2017正則2018'; str = str.replace('正則','正則匹配'); str = str.replace('正則','正則匹配'); //=>'正則匹配匹配2017正則2018' 沒(méi)有實(shí)現(xiàn)我們希望的效果//=>在不使用正則的情況下,執(zhí)行一次replace只能替換一個(gè)原有字符,第二次執(zhí)行replace,還是從字符串的開(kāi)始位置查找,把最新找到的字符替換為新字符(類(lèi)似于正則捕獲時(shí)候的懶惰性:每一次執(zhí)行都是從字符串最開(kāi)始的位置查找) 復(fù)制代碼真實(shí)項(xiàng)目中,replace一般都是和正則搭配在一起使用的
var str = '正則2017正則2018'; str = str.replace(/正則/g,'正則匹配'); //=>"正則匹配2017正則匹配2018" 復(fù)制代碼replace原理: 1、當(dāng)replace方法執(zhí)行,第一項(xiàng)傳遞一個(gè)正則 正則不加g:把當(dāng)前字符串中第一個(gè)和正則匹配的結(jié)果捕獲到,替換成新的字符 正則加g:把當(dāng)前字符串中所有和正則匹配的內(nèi)容都分別的捕獲到,而且每一次捕獲,都會(huì)把當(dāng)前捕獲的內(nèi)容替換為新字符
2、當(dāng)replace方法執(zhí)行,第二個(gè)參數(shù)傳遞的是一個(gè)函數(shù)(回調(diào)函數(shù)) 首先用正則到字符串中進(jìn)行查找匹配,匹配到一個(gè)符合規(guī)則的,就把傳遞的函數(shù)執(zhí)行一次 不僅執(zhí)行這個(gè)函數(shù),而且還把正則本次捕獲的結(jié)果(和執(zhí)行exec捕獲的結(jié)果一樣:數(shù)組、大正則匹配、小分組匹配 都有)當(dāng)做實(shí)參傳遞給這個(gè)函數(shù)(這樣就可以在函數(shù)中獲取這些值:而這些值就是正則每一次捕獲的結(jié)果 )
var str = 'my name is {0},i am {1} years old,i can {2}!'; var reg = /\{(\d+)\}/g; str.replace(reg, function () {//=>傳遞的函數(shù)一共被執(zhí)行三次//=>console.log(arguments) 每一次匹配捕獲到結(jié)果,不僅把這個(gè)方法執(zhí)行了,而且還會(huì)把當(dāng)前捕獲的結(jié)果當(dāng)做實(shí)參傳遞給這個(gè)函數(shù)(ARG)/** 第一次執(zhí)行函數(shù),獲取的是ARG類(lèi)數(shù)組* 0:'{0}' 本次大正則匹配的結(jié)果* 1:'0' 本次第一個(gè)小分組匹配的結(jié)果* 2:11 本次大正則匹配結(jié)果在字符串中的索引 index* 3:'my nam...' 原始字符串 input** 和每一次執(zhí)行exec實(shí)現(xiàn)捕獲的結(jié)果非常類(lèi)似*///return xxx;//=>每一次執(zhí)行函數(shù),函數(shù)中RETURN的結(jié)果,都相當(dāng)于把本次大正則匹配的內(nèi)容替換掉(原始字符串不變) }); 復(fù)制代碼總結(jié)
以上是生活随笔為你收集整理的正则表达式从基础到深入实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C# SqlBulkCopy数据批量入库
- 下一篇: qt不能调试