一篇文章教你玩轉正則

首先了解什麼是正則

regular expression : RegExp /ˈreɡjələr/ /ɪk'sprɛʃən/ 用來處理字符串的規則javascript

  • 只能處理字符串
  • 它是一個規則:能夠驗證字符串是否符合某個規則,也能夠把字符串中符合規則的內容捕獲到(test/exec/match...);
let str = 'good good study,day day up!';
=> 學正則就是用來制定規則;
let reg = /\d+/;
reg.test(str);   =>false;

str = '2019-08-12';
reg.exec(str);   =>["2019", index: 0, input: "2019-08-12", groups: undefined]
複製代碼

編寫正則表達式

建立方式有兩種:java

=> 一、字面量建立方式 兩個斜杆之間包起來的,都是用來描述規則的元字符; let reg1 = /\d+/ ;正則表達式

=>二、構造函數模式建立
兩個參數:元字符字符串,修飾符字符串; let reg2 = new RegExp('\\d+');算法

正則表達式由兩個部分組成

  • 元字符
  • 修飾符

經常使用的元字符

  • 一、量詞元字符:設置出現的次數
  • * 表示0次或者屢次,等同於{0,},即c* 和c{0,} 是一個意思。
  • + 表示一次或者屢次,等同於{1,},即c+ 和c{1,} 是一個意思。 最後;
  • ? 表示0次或者1次,等同於{0,1},即c? 和c{0,1} 是一個意思。;
  • {n} 出現n次;
  • {n,} 出現n 到屢次
  • {n,m} 出現n到m次數;
  • 二、特殊元字符:單個或者組合在一塊兒表明特殊的含義;
  • \ 轉譯字符(普通->特殊->普通)
  • . 除\n (換行符)之外的任意字符;
  • ^ 以哪個元字符做爲開始;
  • $ 以哪個元字符做爲結束;
  • \n 換行符;
  • \d 0~9之間的一個數字;
  • \D 匹配一個非數字字符。等價於 ^0-9
  • \w 數字、字母、下劃線中任意一個字符;
  • \s 一個空白字符(包含空格、製表符、換頁符等)
  • \t 一個製表符(一個TAB鍵:四個空格);
  • \b 邊界符; 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 能夠匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
    • x|y x或者y中的一個字符;
    • [xyz] x或者y或者z中的一個字符;
    • [^xy] 除了x、y之外的任意字符;
    • [a-z]指定a-z這個範圍中的任意字符 [0-9a-zA-Z_]=== \w
    • [^a-z] 上一個的取反「非」;
    • () 正則中的分組符號;
    • (?:) 只匹配不捕獲
    • (?=) 正向預查
    • (?!) 負向預查
  • 三、普通元字符
    • /hahaha/ 此正則匹配的=> "hahaha"

經常使用修飾符

i => ignoreCare 忽略單詞大小寫匹配;express

m =>multiline 能夠進行多行匹配;數組

g => global 全局匹配bash

/A/.test('lalala') =>falseide

/A/i.test('lalala') =>true函數

元字符詳細解析

^ $ui

  • ^/$ 兩個都不加:字符串中包含符合規則的內容便可;
let str = '146576567djkljkljl6787897'`
let reg1 = /\d+/;`只要有數字就能夠了)

reg1.test(str)  // true
複製代碼
  • ^/$ 兩個都加:字符串只能是和規則一致的內容;
let reg2 = /^\d+$/;(從頭至尾必須所有都是數字)

reg2.test(str) // false
複製代碼

例子:驗證手機號碼(11位,第一個數字是1既可);

let reg = /^1\d{10}$/;

let reg = /^\d/; 

reg.test('world');     =>false
reg.test('2019world');   =>ture
reg.test('world2019');   =>false

let reg = /\d$/;
reg.test('world');     =>false
reg.test('2019world');   =>false
reg.test('world2019');   =>ture
複製代碼

\ .不是小數點,是除\n外的任意字符

let reg = /^2.3$/;
console.log(reg.test("2.3"));//=>true
console.log(reg.test("2@3"));//=>true
console.log(reg.test("23"));//=>false

//=>基於轉義字符,讓其只能表明小數點
reg = /^2\.3$/;
console.log(reg.test("2.3"));//=>true
console.log(reg.test("2@3"));//=>false

let str = "\\d";
reg = /^\d$/; //=>\d表明0-9的數字
console.log(reg.test(str)); //=>false
reg = /^\\d$/; //=>把特殊符合轉換爲普通的(\把\d中的\轉移成\自己=> 只能是 \d)
console.log(reg.test(str)); //=>true
複製代碼

x|y

let reg = /^18|29$/;  // => 以1開頭 9 結尾  =>中間是8或者2 // 以18開頭 29結尾均可以,條件不少
console.log(reg.test("18")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("129")); //=>true
console.log(reg.test("189")); //=>true
console.log(reg.test("1829")); //=>true
console.log(reg.test("829")); //=>true
console.log(reg.test("182")); //=>true
//---直接x|y會存在很亂的優先級問題,通常咱們寫的時候都伴隨着小括號進行分組,由於小括號改變處理的優先級 =>小括號:分組
reg = /^(18|29)$/;
console.log(reg.test("18")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("129")); //=>false
console.log(reg.test("189")); //=>false
//=>只能是18或者29中的一個了
複製代碼

[]

//1.中括號中出現的字符通常都表明自己的含義;
let reg = /^[@+]$/;   //   出現@ 或者 +其中一個
console.log(reg.test("@")); //=>true
console.log(reg.test("+")); //=>true
console.log(reg.test("@@")); //=>false
console.log(reg.test("@+")); //=>false

//=>\d在中括號中仍是0-9
reg = /^[\d]$/; 
console.log(reg.test("d"));//=>false
console.log(reg.test("\\"));//=>false
console.log(reg.test("9"));//=>true

//2.中括號中不存在多位數
reg = /^[18]$/;
console.log(reg.test("1")); //=>true
console.log(reg.test("8")); //=>true
console.log(reg.test("18")); //=>false

reg = /^[10-29]$/; //=>1或者0-2或者9
console.log(reg.test("1"));//=>true
console.log(reg.test("9"));//=>true
console.log(reg.test("0"));//=>true
console.log(reg.test("2"));//=>true
console.log(reg.test("10"));//=>false
複製代碼

經常使用的正則表達式

一、驗證是否爲有效數字

/* * 規則分析 * 1.可能出現 + - 號,也可能不出現 [+-]? * 2.一位0-9均可以,多位首位不能是0 (\d|([1-9]\d+)) * 3.小數部分可能有可能沒有,一旦有後面必須有小數點+數字 (\.\d+)? */
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/複製代碼

二、驗證密碼

//=>數字、字母、下劃線
//=>6~16位
let val = userPassInp.value,
    reg = /^\w{6,16}$/;
let flag=reg.test(val);

/*
function checkPass(val){
    if(val.length<6 || val.length>16){
    	alert('長度必須介於6-16位之間!');
    	return;
	}
    let area=['a','b'....'_']; //=>包含數字、字母、下劃線
    for(let i=0;i<val.length;i++){
        let char=val[i];
        if(!area.includes(char)){
            alert('格式不正確!');
    		return;
        }
    }
}
*/
複製代碼

三、驗證真實姓名的

* 1.漢字  /^[\u4E00-\u9FA5]$/
 * 2.名字長度 2~10位
 * 3.可能有譯名 ·漢字  (·[\u4E00-\u9FA5]{2,10}){0,2}
 */
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/;
複製代碼

四、驗證郵箱的

let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;

//=> \w+((-\w+)|(\.\w+))*
	1.開頭是數字字母下劃線(1到多位)
	2.還能夠是 - 數字字母下劃線 或者 .數字字母下劃線,總體零到屢次
//=>郵箱的名字由「數字、字母、下劃線、-、.」幾部分組成,可是-/.不能連續出現也不能做爲開始;

/^\w+/

//=> @[A-Za-z0-9]+
//1.@後面緊跟着:數字、字母 (1-多位)

//=> ((\.|-)[A-Za-z0-9]+)*
//1.對@後面名字的補充
// 多域名     .com.cn
// 企業郵箱    zxt@zhufeng-peixun-office.com

//=> \.[A-Za-z0-9]+
//1. 這個匹配的是最後的域名(.com/.cn/.org/.edu/.net...)
複製代碼

五、身份證號碼

/*
 * 1. 一共18位
 * 2. 最後一位多是X
 *
 * 身份證前六位:省市縣  130828
 * 中間八位:年月日
 * 最後四位:
 *   最後一位 => X或者數字
 *   倒數第二位 => 偶數 女  奇數 男
 *   其他的是通過算法算出來的
 */
//let reg = /^\d{17}(\d|X)$/;
//=>小括號分組的第二個做用:分組捕獲,不只能夠把大正則匹配的信息捕獲到,還能夠單獨捕獲到每一個小分組的內容
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
reg.exec("130828199012040617"); //=>["130828199012040617", "130828", "1990", "12", "04", "1", "7"...] 捕獲結果是數組,包含每個小分組單獨獲取的內容
複製代碼

正則兩種建立方式的區別

  • 構造函數由於傳遞的是字符串,\須要寫兩個才表明斜槓 let reg = /\d+/g;

    reg = new RegExp("\\d+","g");

  • 正則表達是中的部份內容是變量存儲的值

    1.兩個斜槓中間包起來的都是元字符(若是正則中要包含某個變量的值,則不能使用字面量方式建立)

let type = "hahahaha";
reg = /^@"+type+"@$/;   // 以@ 開始/結束," 出現屢次 typ e出現屢次」 
console.log(reg.test("@hahaha@")); //=>false
console.log(reg.test('@"""typeeeee"@')); //=>true
//2.這種狀況只能使用構造函數方式(由於它傳遞的規則是字符串,只有這樣才能進行字符串拼接)
reg = new RegExp("^@"+type+"@$");   // type 爲變量;
console.log(reg.test("@hahahaha@"));//=>true
複製代碼

正則的捕獲

實現正則捕獲的辦法

  • 正則RegExp.prototype上的方法
    • exec
    • test
  • 字符串String.prototype上支持正則表達式處理的方法
    • replace
    • match
    • splite
    • ...
let str = "lyridel2019yangfan2020qihang2021";
let reg = /\d+/;
複製代碼

正則懶惰性

  • 基於exec實現正則的捕獲
  • 1.捕獲到的結果是null或者一個數組
  • 第一項:本次捕獲到的內容
  • 其他項:對應小分組本次單獨捕獲的內容
  • index:當前捕獲內容在字符串中的起始索引
  • input:原始字符串
  • 2.每執行一次exec,只能捕獲到一個符合正則規則的,可是默認狀況下,咱們執行一百遍,獲取的結果永遠都是第一個匹配到的,其他的捕獲不到

=>「正則捕獲的懶惰性」:默認只捕獲第一個捕獲的內容

懶惰性緣由

let str = "ddhwe2019yangfan2020qihang2021";

- reg.lastIndex:當前正則下一次匹配的起始索引位置 
- 懶惰性捕獲的緣由:默認狀況下lastIndex的值不會被修改,每一次都是從字符串開始位置查找,因此找到的永遠只是第一個
let reg = /\d+/;
console.log(reg.lastIndex); //=>0 下面匹配捕獲是從STR索引零的位置開始找
console.log(reg.exec(str));
console.log(reg.lastIndex); //=>0 第一次匹配捕獲完成`
lastIndex沒有改變,因此下一次exec依然是從字符串最開始找,找到的永遠是第一個匹配到的
複製代碼

懶惰性的解決辦法

解決辦法:全局修飾符g

可是需注意:匹配最後一次,沒有以後會返回null,再匹配lastIndex爲0 開始尋找

let reg = /\d+/g;
console.log(reg.exec(str)); //=>["2019"...]
console.log(reg.lastIndex); //=>11 設置全局匹配修飾符g後,第一次匹配完,lastIndex會本身修改
console.log(reg.exec(str)); //=>["2020"...]
console.log(reg.lastIndex); //=>22
console.log(reg.exec(str)); //=>["2021"...]
console.log(reg.lastIndex); //=>32
console.log(reg.exec(str)); //=>null 當所有捕獲後,再次捕獲的結果是null,可是lastIndex又迴歸了初始值零,再次捕獲又從第一個開始了...
console.log(reg.lastIndex); //=>0
console.log(reg.exec(str)); //=>["2019"...]
複製代碼

貪婪性

每一次捕獲都按照正則匹配的最長結果捕獲

let str = 'jkldjkl1234';
let reg = /\d+/g;

+ 表明1-多位,可是正則匹配只按照最長的匹配
複製代碼

貪婪性解決方案

從新封裝EXEC

需求:編寫一個方法execAll,執行一次能夠把全部匹配的結果捕獲到(前提正則必定要設置全局修飾符g)

~ function () {
	function execAll(str = "") {
		//=>str:要匹配的字符串
		//=>this:RegExp的實例(當前操做的正則)
		//=>進來後的第一件事,是驗證當前正則是否設置了G,不設置則不能在進行循環捕獲了,不然會致使死循環
		if (!this.global) return this.exec(str);
		//=>ARY存儲最後全部捕獲的信息 RES存儲每一次捕獲的內容(數組)
		let ary = [],
			res = this.exec(str);
		while (res) {
			//=>把每一次捕獲的內容RES[0]存放到數組中
			ary.push(res[0]);
			//=>只要捕獲的內容不爲NULL,則繼續捕獲下去
			res = this.exec(str);
		}
		return ary.length === 0 ? null : ary;
	}
	RegExp.prototype.execAll = execAll;
}();
let reg = /\d+/g;	
console.log(reg.execAll("DD2019@2020不少3GD")); //=> ["2019", "2020", "3"]
複製代碼

//=>字符串中的MATCH方法,能夠在執行一次的狀況下,捕獲到全部匹配的數據(前提:正則也得設置G才能夠)

正則的分組捕獲

//=>身份證號碼
let str = "130828199012040112";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str));
console.log(str.match(reg));
//=>["130828199012040112", "130828", "1990", "12", "04", "1", index: 0, input: "130828199012040112"]
//=>第一項:大正則匹配的結果
//=>其他項:每個小分組單獨匹配捕獲的結果
//=>若是設置了分組(改變優先級),可是捕獲的時候不須要單獨捕獲,能夠基於?:來處理
複製代碼
//=>既要捕獲到{數字},也想單獨的把數字也獲取到,例如:第一次找到 {0} 還須要單獨獲取0
let str = "{0}年{1}月{2}日";
​
/* //=>不設置g只匹配一次,exec和match獲取的結果一致(既有大正則匹配的信息,也有小分組匹配的信息) let reg = /\{(\d+)\}/; console.log(reg.exec(str)); console.log(str.match(reg)); //["{0}", "0",...] */let reg = /\{(\d+)\}/g;
//console.log(str.match(reg)); //=>["{0}", "{1}", "{2}"] 屢次匹配的狀況下,match只能把大正則匹配的內容獲取到,小分組匹配的信息沒法獲取
let aryBig=[],
    arySmall=[],
    res=reg.exec(str);
while(res){
    let [big,small]=res;
    aryBig.push(big);
    arySmall.push(small);
    res=reg.exec(str);
}
console.log(aryBig,arySmall); //=>["{0}", "{1}", "{2}"] ["0", "1", "2"]
複製代碼
相關文章
相關標籤/搜索