正則regular

/***
 * 正則regular
 */
/**
 * //1.正則初體驗
 * //1.1什麼是正則
 * //簡單來說正則就可以理解爲一個規則,用來處理字符窗的一個規則(正則就是用來處理字符串的),這裏所說的處理一般包含匹配和捕獲
 */

//匹配:判斷一個字符串是否符合指定的規則,使用test方法:reg.test(str)
//eg.1.
var reg = /\d/;     //包含一個0-9之間的數字
console.log(reg.test('天'));     //false
console.log(reg.test('1'));     //true
console.log(reg.test('現在是2017'));     //true,只要包含了數字就返回true
//捕獲:把字符串中符合指定的正則規則的內容捕獲到,使用exec方法:reg.exec(str)
//eg.2.
 var reg = /\d/;
console.log(reg.exec('天'));     //null
console.log(reg.test('1'));     //["1",index:0,input:"1"]
console.log(reg.test('現在是2017'));     //["2",index:3,input:"現在是2017"]
//1.2創建正則
//字面量方式
var reg = /\d/;
//實例方式
var reg = new RegExp('/\d/');

//數組對象等的字面量方式創建和實例方式創建出來的數組差別並不是很大,但是正則的這兩種方式創建出來是有很大的區別的。具體的區別後文會講到

/**
 * //2.正則的組成
 * //每一個正則表達式是包含在//中的,正則表達式就是匹配規則,正則的組成就是源字符和修飾符。
 */
/**
 * 2.1元字符
 * 具有特殊意義的元字符
 * \:轉義字符,轉義後面字符所代表的含義
 * ^:以某一個元字符開始
 * $:以某一個元字符結束
 * \n:匹配一個換行符
 * .:除了\n以外的任意字符
 */
//eg.1.
var reg = /^0.2$/;  //以0開頭,以2結尾,中間可以是除了\n的任意一個字符
console.log(reg.test('0.2'));   //true
console.log(reg.test('0-2'));   //true
console.log(reg.test('0...2'));   //false 中間有三個字符字符,
console.log(reg.test('0-.2'));   //false 中間有兩個字符字符,

reg = /^0\.2$/;     //將"."轉轉義
console.log(reg.test('0.2'));   //true
console.log(reg.test('0-2'));   //false
console.log(reg.test('0..2'));   //false 只能匹配0.2

/**
 * 代表出現次數的量詞元字符
 * *:出現0到多次
 * +:出現1到多次
 * ?:出現0次或者1次
 * {n}:出現n次
 * {n,m}:出現n到m次
 */
//eg.2.
var reg = /^\d+$/;
console.log(reg.test('2015'));  //true

/**
 * 2.2修飾符
 * x|y:x或y中的一個
 * [xyz]:x或y或z中的一個
 * [^xyz]:除了xyz以外的任意一個字符
 * [a-z]:a-z之間的任何一個字符
 * \d:一個0-9之間的數字
 * \D:除了0-9之間數字以外的任何字符
 * \b:一個邊界符
 * \w:數字、字母、下劃線中的任意一個字符
 * \s:匹配一個空白字符、空格
 * ():分組,把一個大正則本身劃分成幾個小的正則,例如:var reg = /^(\d+)zhufeng(\d+)$/;
 */

/**
 * 3元字符的應用
 * 在做元字符的應用前,有必要先了解下中括號和分組的使用,然後能更好的做應用。
 */

/**
 * 3.1[]的規律
 * 在中括號中出現的所有字符都是代表本身的意思的字符(沒有特殊含義)
 */

//eg.1.
var reg = /^[.]$/;
console.log(reg.test('1'));     //false
console.log(reg.test('.'));     //true

reg = /^[\w-]$/;    //數字、字母、下劃線、-中的一個
console.log(reg.test('-'));     //true

//中括號不時別兩位數
var reg = /^[12]$/  //1或者2中的一個(符合[xyz])
var reg = /^[12-68]$/;  //1,2-6,8 三個中的一個
console.log(reg.test('30')); //false
console.log(reg.test('3')); //true
console.log(reg.test('7')); //false
console.log(reg.test('9')); //false

/**
 * ()的作用
 * 分組的作用有很多,現在先講其中的一個:改變x|y的默認優先級,還有的在後面的內容會詳細介紹
 */

var reg = /^18|19$/;    //18,19,181,189,119,819,1819這些都符合
var reg = /^(18|19)$/;  //只能18或者19

/**
 * 3.3應用一:有效數字的正則
 * 有效數字可以是正數、負數、零、小數,所以其特點爲:
 * "."可以出現也可以不出現,一旦出現,後面必須跟着一位或多位數字;
 * 最開始有"+/-",也可以沒有;
 * 整數部分,一位數的情況可以是0-9中的一個,多位數的情況下不能以0開頭
 */
var reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;

/**
 * 應用二:年齡介於18-65之間
 * 年齡介於18-65之間的數字可以是18-19、20-59、60-65
 */
var reg = /^1[8-9]|[2-5]\d|6[0-5]/;
console.log(reg.exec(35));  //["35", index: 0, input: "35"]

var reg = /^(1[8-9])|([2-5]\d)|(6[0-5])/;
console.log(reg.exec(35));  //["35", undefined, "35", undefined, index: 0, input: "35"]

/**
 * 應用三:簡單的郵箱郵箱驗證
 */
var reg = /^[\w.-]+@[0-9a-zA-Z]+(\.[a-zA-Z]{2,4}){1,2}$/;
console.log(reg.exec("[email protected]")); //["[email protected]", ".cn", index: 0, input: "[email protected]"]

/**
 * 4兩種方式創建正則的區別
 * 在開始的時候,我們介紹了創建正則有兩種方式:字面量方式、對象方式、。在字面量方式中,“//”之間包起來的所有的內容都是元字符,有的具有特殊的意義,大部分都是代表本身含義的普通元字符
  */

//現在有一個場景,就是正則中的某一段內容是不固定的,那麼我們用字面量的方式可能會這麼寫:
var name = 'iceman';
var reg = /^\d+"+name+"\d+$/;
console.log(reg.test('2015iceman2016'));    //false
console.log(reg.test('2015"""nameee"2016'));    //true

/**
 * 我們的想法很美好,假設name是動態設置的,內容爲"iceman",那麼'2015iceman2016'是肯定能適配reg的啊,但結果卻是false。
 * 第二條輸出的卻是true!是不是很崩潰呢?不過再認真看一下,我們在第二條匹配的字符串中寫了三個引號,name的後面再加了三個e。看到這裏是不是發現了什麼呢
 * 沒錯在字面量方式創建的正則中,引號和單獨出現的加號都被當成了普通的元字符。
 * 對於上面這個需求,我們只能使用實例創建正則的方式:
 */
var name = 'iceman';
var reg = new RegExp("^\\d"+name+"\\d+$",'g');
console.log(reg.test('2015iceman2016'));    //true

/**
 * 所以總結字面量方式和實例方式創建正則的區別:
 * 字面量方式中出現的一切都是元字符,不能進行變量值的拼接,而實例創建的方式可以;
 * 字面量方式中直接寫\d可以,而實例中需要把它轉義\\d
 */

/**
 * 5正則捕獲及其貪婪行和懶惰性
 * 在上面介紹到正則的捕獲使用exec方法。在每一次捕獲的時候都是先進行默認的匹配,如果沒有匹配成功,則捕獲的結果是null。只有匹配的內容,才能捕獲到。
 *
 * 5.1 懶惰性
 */

var reg = /\d+/;
var str = 'icemane2016learn2017';
//reg默認有一個lastIndex字段,該字段是正則每一次捕獲時,在字符串中開始查找的位置,默認的值是0.
//現在先進行第一次捕獲:
console.log(reg.lastIndex);     //0,第一次捕獲的時候,從字符串索引0處開始查找
var res = reg.exec(str);
console.log(res);   //["2016", index: 7, input: "icemane2016learn2017"]
/**
 * 從代碼的輸出可知,正則捕獲的內容格式:捕獲到的內容是一個數組
 * 數組的第一項是當前正則捕獲的內容;
 * 有一項是index:捕獲內容的字符串中開始的索引位置;
 * 有一項是input:捕獲原始字符串;
 */
//現在進行第二次捕獲:
console.log(reg.lastIndex); //0 說明第二次捕獲的時候也要從字符串索引0處開始查找
//第二次通過exec捕獲的內容還是第一個"2016"res=reg.exec(str);
console.log(res);   //["2016", index: 7, input: "icemane2016learn2017"]

/**
 * 由上面的兩次捕獲可知,每次的捕獲都是從字符串的索引0處開始查找的,這就是正則的懶惰性。
 * 正則懶惰性的特點:每一次執行exec只捕獲第一個匹配的內容,在不進行任何處理的情況下,在執行多次捕獲的時候,捕獲的還是第一個匹配的內容。
 * 很明顯正則的懶惰性是我們所要解決的問題,那麼該如何解決懶惰性呢?答案就是在正則的末尾加一個修飾符"g"(全局匹配),類似g這樣的修飾符還有兩個:i,m這兩者的作用分別是:
 * global(g):全局匹配
 * ignoreCase(i):忽略大小寫
 * multiline(m):多行匹配
 */
var reg = /\d+/g;
var str = 'iceman2016learn2017';
console.log(reg.lastIndex);     //0
console.log(reg.exec(str));     //["2016", index: 6, input: "iceman2016learn2017"]
console.log(reg.lastIndex);     //10
console.log(reg.exec(str));     //["2017", index: 15, input: "iceman2016learn2017"]
console.log(reg.lastIndex);     //19
console.log(reg.exec(str));  //null

/**
 * 加了修飾符g之後,就解決了懶惰性,達到了我們想要的效果,所以全局修飾符g的原理是:正則每一次捕獲結束後,lastIndex的值都變成了最新的值,下一次捕獲從最新的位置開始查找,這樣就可以把所有需要捕獲的內容都獲取到了。
 * 自己編寫程序獲取正則捕獲的所有內容(正則一定要加g哦!!!)
 */
var reg = /\d+/g;
var str = 'iceman2016learn2017';
var ary = [];
var res = reg.exec(str);
while(res){
    ary.push(res[0]);
    res = reg.exec(str);
}
console.log(ary);   //["2016", "2017"]

/**
 * 5.2 貪婪性
 * @type {RegExp}
 */

var reg = /\d+/g;   //出現一到多個0-9之間的數字
var str = 'iceman2016learn2017javascript2018';
console.log(reg.exec(str));     //["2016", index: 6, input: "iceman2016learn2017javascript2018"]
console.log(reg.exec(str));     //["2017", index: 15, input: "iceman2016learn2017javascript2018"]

/**
 * 看到這段代碼的時候不知道您有沒有一些疑惑,正則的內容是/\d+/,是匹配1到多個數字,2015是符合正則,那麼2也是符合正則的啊,爲什麼默認就捕獲了2015呢?這就是正則的貪婪性。
 * 如何解決正則的貪婪性:在量詞元字符後面添加一個"?"即可。
 * @type {RegExp}
 */


var reg = /\d+?/g;
var str = 'iceman2016learn2017';
var ary = [];
var res = reg.exec(str);
while(res){
    ary.push(res[0]);
    res = reg.exec(str);
}
console.log(ary);   //["2", "0", "1", "6", "2", "0", "1", "7"]
/**
 * "?"在正則中的作用:
 * 放在一個普通的元字符後面,代表出現0-1次;
 * 放在一個量詞的元字符後面,取消捕獲時候的貪婪性
 */

/**
 * 字符串中的match方法
 * match方法的作用是,把所有和正則的字符都獲取到。
 */
var reg = /\d+?/g;
var str = 'zhufeng2015peixun2016dasgdas2017';
var ary = str.match(reg);
console.log(ary);   //["2", "0", "1", "5", "2", "0", "1", "6", "2", "0", "1", "7"]

/**
 * 注意:雖然在當前的情況下。match比exec更加簡潔一些,但是match存在一些自己處理不了的問題:在分組捕獲的情況下,match只能捕獲到大正則,而對於小正則捕獲的內容是無法獲取的。
 */

/**
 * 6 分組捕獲
 * 6.1正則分組
 * 正則分組的兩個作用:
 * 改變優先級(在“三、元字符的應用”中已經有介紹到)
 * 分組引用
 * \2代表和第二個分組出現一模一樣(和對應地分組中的內容和值都要一樣)的內容,\1代表和第一個分組出現一模一樣的內容;
 */
var reg = /^(\w)(\w)\1\2$/;
console.log(reg.test("icic"));  //true
console.log(reg.test("r0g_"));  //false

/**
 * 6.2 分組捕獲
 * 正則在捕獲的時候,不僅僅把大正則匹配的內容捕獲到,而且還可以把小分組匹配的內容捕獲到。
 * 身份證中的數字都有意義的,比如開頭的兩位代表省,中間的四位代表。。。所以對於一個身份中,有必要對其中的數字按照其意義進行分組捕獲。
 */

var reg = /^(\d{2})(\d{4})(\d{4})(\d{2})(\d{2})(?:\d{2})(\d)(?:\d|X)$/;
var str = "350324202904190216";
console.log(reg.exec(str));     //["350324202904190216", "35", "0324", "2029", "04", "19", "1", index: 0, input: "350324202904190216"]

//注意:(?:)在分組中?:的意思是隻匹配不捕獲
/**
 * 輸出的內容爲["350324202904190216", "35", "0324", "2029", "04", "19", "1", index: 0, input: "350324202904190216"]
 * 其中:
 *   350324202904190216:大正則匹配的內容
 *   35:第一個分組捕獲的內容
 *   0324:第二個分組捕獲的內容
 *   。。。
 */
//在這裏使用match方法的話,和exec獲取的內容一樣:
console.log(str.match(reg));
//再看一個例子:
var reg = /ice(\d+)/g;
var str = 'ice1234ice3456ice5678';  //用exec執行三次,每一次不僅僅把大正則匹配的獲取到,而且還可以獲取第一個分組匹配的內容
console.log(reg.exec(str));     //["ice1234", "1234", index: 0, input: "ice1234ice3456ice5678
console.log(reg.exec(str));     //["ice3456", "3456", index: 7, input: "ice1234ice3456ice5678"]
console.log(reg.exec(str));     //["ice5678", "5678", index: 14, input: "ice1234ice3456ice5678"]
//而match只能捕獲大正則
console.log(str.match(reg));    //["ice1234", "ice3456", "ice5678"]
//此處match是隻能捕獲大正則的內容,所以match能做到的exec都能做到,match做不到的exec也能做到
//總結:只捕獲一次就好了,那麼用exec和match都可以,像本例中要捕獲三次的,用match就捕獲不到小正則了。

/**
 * 7replace基礎
 */

var str = 'iceman2016iceman2017';
//在上面定義的字符串中,現在需要將iceman替換成shoushou,我們知道字符串提供了一個replace方法,那麼我們用replace來做一次:
str = str.replace('iceman','shoushou');
console.log(str);   //shoushou2016iceman2017

/**
 * 從打印的結果可知:並沒有得到我們想要的效果,只替換了第一個iceman字符串,看MDN中對於replace方法的定義:
 * str.replace(regexp|substr,newSubStr|function)
 * replace()方法返回一個由替換一些或所有匹配的模式後的新字符串。模式可以是一個字符串或者一個正則表達式,替換值可以是一個字符串或者一個每次匹配都要調用的函數。
 * 由replace的定義可知,匹配模式可以是一個正則的,所以我們用正則試一次:
 */
str = str.replace(/iceman/g,'shoushou');
console.log(str);   //shoushou2016shoushou2017
//使用正則是時候已經實現了需求,並且注意要使用修飾符g讓正則全局捕獲。

//replace第一項的值是正則的情況下實現原理:首先和exec捕獲一樣,把所有和exec捕獲一樣,把所有和正則匹配的內容都捕獲到,然後把捕獲的內容替換成我們需要替換的新內容,在這裏就是按照/iceman/g把str中所有可以匹配到的都捕獲到,然後替換成iceman.
//再看replace函數的定義,第二個參數可以是一個函數:
var str = 'iceman2016iceman2017';
str = str.replace(/iceman/g,function(){
    //第一次執行匿名函數輸出arguments的結果:["iceman", 0, "iceman2016iceman2017"]
    //第二次執行匿名函數輸出arguments的結果:["iceman", 10, "iceman2016iceman2017"]
    console.log(arguments);
    return 'shoushou';
});
console.log(str);   //shoushou2016shoushou2017

/**
 * 從打印結果可知:
 *    先按照正則指定的規則,到字符串中把正則匹配的內容捕獲到,然後在每第一次捕獲之後,都把捕獲到的內容替換成新的內容:
 *    匿名函數執行多少次,取決於正則能在字符串中捕獲多少次,在這裏正則捕獲量詞,所以匿名函數也執行兩次。
 *    每一次執行匿名函數,裏面傳遞的參數值arguments和自己通過exec捕獲到的結果是一樣的(每一次執行匿名函數,和單獨執行exec捕獲的內容一致);
 *    小分組捕獲的內容,在這裏同樣可以或取到(所以說,replace和exec原理是一模一樣的,比match要強大)
 *    return的結果是什麼,就相當於把當前這一次大正則捕獲的內容替換成返回的內容。如果不寫return,默認使用undefined來進行替換,如果不想實現替換的話,我們可以把捕獲的內容再返回回去return arguments[0];
 */

var str = 'iceman2016iceman2017';
str = str.replace(/iceman/g,function(){
    //第一次執行匿名函數輸出arguments的結果:["iceman", 0, "iceman2016iceman2017"]
    //第二次執行匿名函數輸出arguments的結果:["iceman", 10, "iceman2016iceman2017"]
    console.log(arguments);
    // return 'shoushou';
});
console.log(str);   //undefined2016undefined2017

var str = 'iceman2016iceman2017';
str = str.replace(/iceman/g,function(){
    //第一次執行匿名函數輸出arguments的結果:["iceman", 0, "iceman2016iceman2017"]
    //第二次執行匿名函數輸出arguments的結果:["iceman", 10, "iceman2016iceman2017"]
    console.log(arguments);
    return arguments[0];
});
console.log(str);   //iceman2016iceman2017

//從打印中可知,當有分組的時候,arguments的第二個參數開始分組的內容,所以可以用arguments[1]這樣的方式來獲取分組的內容。

/**
 * 8 replace實戰
 * 從前面的介紹中已經知道,正則的捕獲有三種方式:正則的exec方法、字符串的match方法、字符串的replace方法。
 * 其中replace是將原有的字符串替換成我們想要的新的字符串,在不適用正則的情況下,執行一次replace只能替換字符串中的一個,而使用正則的話,可以一次批量的把所有的正則匹配的內容都替換掉。
 *
 * 8.1 實戰一:小寫數字替換成大寫數字
 */
var str = '今年是2017年';  //
var ary = ['零','貳','叄','肆','伍','陸','柒','捌','玖','拾'];
//實現替換的話,需要捕獲到數字,並且把數字當作ary的索引獲取對應的漢字進行替換
str = str.replace(/\d/g,function(){
    /**
     * 第一次執行:大正則捕獲的是2,返回的是ary[2]-->'貳'
     * 第二次執行:大正則捕獲的是2,返回的是ary[0]-->'零'
     * 。。。
     */
    return ary[arguments[0]];
});
console.log(str);

/**
 * 實戰二:獲取一個字符串中出現次數最多的字符,並且獲取出現的次數
 */
var str = 'zhongguofujianxiamensimingshoushou';
//1)獲取每一個字符出現的次數
var obj = {};
str.replace(/[a-z]/gi,function(){
    var val = arguments[0];
    obj[val] >= 1 ? obj[val] += 1 : obj[val] = 1;
    // console.log("obj=",obj);
});
//2)獲取最多出現的次數
var maxNum = 0;
for(var key in obj){
    obj[key] > maxNum ? maxNum = obj[key]:null;
}
//3)把所有符合出現maxNum次數的都獲取到
var ary = [];
for(var key in obj){
    obj[key] === maxNum ? ary.push(key) : null;
}
console.log('整個字符串中出現次數最多的字符是:'+ary.toString()+'出現了'+maxNum+'次');

/**
 * 8.3 實戰三:模板引擎實現的初步原理
 */
var str = 'my name is {0},my age is {1},i come from {2},i love {3} ~~~';
var ary = ['iceman','26','China','javascript'];
str = str.replace(/{(\d+)}/g,function(){
    return ary[arguments[1]];
});
console.log(str);    //my name is iceman,my age is 26,i come from China,i love javascript ~~~

用js實現千位分隔符
function commafy(num) {
      return num && num
          .toString()
          .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
              return $1 + ",";
          });
  }
  console.log(commafy(1234567.90)); //1,234,567.90


希望我的入坑經驗對你有所幫助,願聖光與你同在

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章