正則講了很久,也拖了很久,今天看看怎么用吧,后續更文應該會比較準勤快了。:-)
書接上文【正則筆記(2)】。這次我們來看看正則的使用:
(注:斜體表示為對應規則寫出的正則表達式)
一、 常用的正則表達式:
1. 驗證是否為有效數字:
規則分析:
a. 可能出現 + - 號,也可能不出現。那么根據該規則我們可以寫出正則:[+-]?
b.如果是1位數字那么0-9都可以,如果是多位數字,則首位不能為0。則我們可以寫出正則:\d|([1-9]\d+)
c.小數部分可能有也可能沒有。一旦有小數部分則必須有小數點+數字 。對應正則:(\.\d+)?
綜合以上的幾個部分,所以我們知道驗證有效數字的正則可以寫成:
let?reg?=?/^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;
通過上面的分析,發現了嗎?寫正則表達式一般我們是按位來寫。
2.驗證密碼的正則:
規則分析:
a. 數字,字母,下劃線
b. 6-16位
為了讓各位看客體驗一下正則的強大,我們先用傳統JS的方法來校驗一下密碼:
//不用正則:let?val?=?userPassInp.value;?//這里表示從密碼框中獲取密碼值function?checkPass(val)?{ if (val.length<6 || val.length>16) { alert('長度必須介于6-16位之間'); return; }??let?area?=?['a','/**...這里表示的是a-z,A-Z,0-9,_*/'];?//?這里應該是包含字母數字下劃線的數組 for(let i=0; i<val.length; i++) { let char = val[i]; if (!area.includes(char)) { alert('密碼格式不正確'); return; } }}
是不是有點冗長?那么來看一下正則校驗密碼:
let reg2 = /^\w{6-16}$/;let?flag?=?reg2.test(val);?//?若 flag?為true則表示正確
簡單多了吧,有沒有分分鐘愛上正則?
3.驗證真實姓名:
規則:
a.漢字。那么應該對應中文第一個漢字到最后一個漢字的Unicode編碼值,即:/^[\u4E00-\u9FA5]$/
b. 名字長度2-10位。即:{2,10}
c.可能有譯名的情況發生,比如:'尼古拉·趙四',·后面暫定1-10位吧,可以酌情修改。(·[\u4E00-\u9FA5]{1,10})?
let?reg?=?/^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{1,10})?$/;reg.test('孫尚香');?//?truereg.test('cos'); // false 必須是中文reg.test('尼古拉·趙四'); // true
4.驗證郵箱:
規則:
a.開頭必須是字母、數字、下劃線 (可以1-多次)。即:? \w+
比如:
????sun-shang-xiang????sun.shang.xiang
????sun-shang.xiang
b.還可以是 -字母數字下劃線 或者 .數字字母下劃線, 整體0到多位。即: ((-\w+)|(\.\w+))*(注:郵箱的名字由數字、字母、下劃線、中劃線、點、幾部分組成,但是 中劃線和點不能連續出現,也不能作為開始)
c. @符后面緊跟著:數字,字母(可以是一直多位)。即: [A-Za-z0-9]+
d. 我們還需要對@符號后面的內容做一些補充,因為:
①有可能是多域名郵箱比如:.com.cn
②也可能企業域名 ssx@shuhan-wangchao-office.com? //孫尚香@蜀漢-王朝-辦公室.com
所以:((\.|-)[A-Za-z0-9]+)*
e-? 匹配最后的域名,比如:(.com .cn .org .net .edu)。即:\.[A-Za-z0-9]+
綜上,郵箱的正則為:
let?reg?=?/^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
5.身份證號碼:
規則:
a.一共18位
b.?最后以為可能是X
c. 身份證前六位是省市縣
d. 中間8位是出生年月日
e. 最后四位:
????最后1位是X或者數字 ?
????倒數第二位偶爾是女 奇數是男
????其余的是經過算法算出來
let?reg?=?/^\d{17}(\d|X)$/;
我們來分析一下上面這一段正則:
在這里我們遇到了 () 的第二個作用:分組捕獲。
不僅可以把大正則匹配的信息捕獲到,還可以單獨捕獲到每個小分組的內容
比如:
let?reg?=?/^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;reg.exec('410521199303088019');?//?正則捕獲??捕獲的結果是個數組?包含了每個小分組單獨獲取的內容
我們來看一下分組捕獲到的具體內容:
0: "410521199303088019"
1: "410521"
2: "1993"
3: "03"
4: "08"
5: "1"
6: "9"
groups: undefined
index: 0
input: "410521199303088019"
length: 7
二、兩種創建正則的方式之間的區別:
1- 字面量創建:
let reg = /\\/;reg.test('\\');?//true//或者這樣:let?reg2?=?/\d+/g;
2- 構造函數模式創建: 這種寫法RegExp有2個參數:元字符字符串 和 修飾符字符串
let?reg?=?new?RegExp('\\d+','g');// ==>構造函數因為傳遞的是字符串,\需要些兩個才代表斜杠。
說明:
1.正則表達式中的部分內容是變量存儲的值
2.兩個斜杠包起來的部分都是元字符 (如果正則中要包含某個變量的值,則不能使用字面量方式創建)
3.如果正則中包含某個變量只能使用構造函數的形式 (因為它傳遞的是字符串,只有這樣才能進行字符串拼接)
舉個栗子叭。
字面量形式:
let?type?=?'cos';let?reg?=?/^@"+type+"@$/;reg.test("@cos@");?//==>?falsereg.test('@"""typeeeee"@');?//==>?true?因為正則中的?+?號被作為元字符了
構造函數形式:
let?reg?=?new?RegExp('^@'+?type?+'@$');reg5.test("@cos@");?// true
三、正則捕獲:
1- 實現正則捕獲的方法:
正則RegExp.prototype上的方法
??????? exec
??????? test
2- 字符串String.prototype上支持正則表達式處理的方法:
????? replace
????? match
????? splite
????? ...
常用正則表達式的方法:
exec:一個在字符串中執行查找匹配的RegExp方法,它返回一個數組(未匹配到則返回 null)。test:一個在字符串中測試是否匹配的RegExp方法,它返回 true 或 false。match:一個在字符串中執行查找匹配的String方法,它返回一個數組,在未匹配到時會返回 null。matchAll:一個在字符串中執行查找所有匹配的String方法,它返回一個迭代器(iterator)。search:一個在字符串中測試匹配的String方法,它返回匹配到的位置索引,或者在失敗時返回-1。replace:一個在字符串中執行查找匹配的String方法,并且使用替換字符串替換掉匹配到的子字符串。split:一個使用正則表達式或者一個固定字符串分隔一個字符串,并將分隔后的子字符串存儲到數組中的 String 方法。
3-正則捕獲的懶惰性:
如果我們想捕獲字符串str中所有的數字:
let str = "cos2019yangfan2020qihang2021";// 捕獲str中所有的數字 (首先正則得匹配才行)// 實現正則捕獲的前提是 當前正則要和對應字符串匹配,如果不匹配捕獲的結果為nulllet reg = /^\d+/;reg.test(str); //==> falsereg.exec(str);?//==>?null 沒有匹配到所以返回nulllet reg2 = /\d+/;reg2.test(str);?//==>?falsereg2.exec(str);?//==>?得到的結果是個數組?["2019",?index:?3,?input:?"cos2019yangfan2020qihang2021",?groups:?undefined]
基于exec實現正則的捕獲:
1-捕獲到的結果是null或者一個數組 。
數組第一項:為本次捕獲到的內容? ?
其余項是對應小分組單獨捕獲的內容
index: 當前捕獲的結果在字符串中的起始位置
input: 原始字符串
2-每執行一次exec方法只能捕獲到一個符合正則規則的,但是默認情況下,我們執行多次,獲取的結果永遠都是第一個匹配到的,其余的捕獲不到,這個叫做正則捕獲的懶惰性。
正則捕獲懶惰性的原理:
這一切都要從RegExpObject.lastIndex 說起,lastIndex 屬性用于規定下次匹配的起始位置。
正則捕獲懶惰性的原因是:默認情況下 lastIndex 的值不會被修改,
每一次都是從字符串開始位置查找,
所以找到的永遠是第一個
解決辦法:使用全局修飾符? g
// exec原理:letstr = "cos2019yangfan2020qihang2021";letreg = /\d+/;reg.lastIndex;?//==>?0?下面匹配捕獲是從str索引為0的位置開始找reg.exec(str);?//==>?['2019',...]reg.lastIndex;?//==>?0?注意:lastIndex沒有改變
第一次匹配捕獲完成,lastIndex沒有改變,所以下一次exec依然是從字符串最開始找,找到的永遠是第一個匹配到的,所有的捕獲方法都遵循這個規則(因為是正則本身的機制)。
正則捕獲懶惰型的解決方案:修飾符 g
let str = "cos2019yangfan2020qihang2021";let reg = /\d+/g;reg.lastIndex;?//==>?0reg.exec(str);?//==>?['2019'...]reg.lastIndex;?//==>?7// 注意:設置全局匹配修飾符g后,第一次匹配完,lastIndex的值會自己修改reg.exec(str);?//==>?['2020'...]reg.lastIndex;?//==>?18reg.exec(str);?//==>?['2021'...]reg.lastIndex;?//==>?28reg.exec(str);?//==>?null?當全部捕獲后,再次捕獲的結果是null,但是lastIndex又回到了初始值0,再次捕獲,又從第一個開始了...reg.lastIndex;?//==>?0reg.exec(str);?//==>?['2019'...]
仔細看會發現,當exec匹配到null之后,又從字符串開端重新開始匹配了。這是因為,當全部捕獲后,再次捕獲的結果是null,但是lastIndex又回到了初始值0,再次捕獲,又從第一個開始了...
重要事項:不具有標志 g 和不表示全局模式的 RegExp 對象不能使用 lastIndex 屬性。
因為我們說只有正則能夠在字符串中匹配到值的情況下,捕獲才有意義,所以可能可能有人會這么想:是不是可以先使用test()方法來檢測一下正則能否匹配到字符串中的值,如果能匹配到值,再進行捕獲。
但是這種方案是不對的:
//?如果字符串不符合正則,那么就無法捕獲// 下面的解決方案是有問題的:let str = "cos2019yangfan2020qihang2021";let reg = /\d+/g;if?(reg.test(str))?{????//?驗證一下:?只有正則和字符串匹配我們再捕獲???console.log(reg.lastIndex);?//?7?基于test匹配驗證后, console.log(reg.exec(str)); // ==> ['2020',...] lastIndex已經被修改為第一次匹配后的結果,所以下一次捕獲不再從頭開始了}
存在的問題: 如果這么寫那么我們就沒有辦法匹配到第一個符合規則的值了。
于是,我們提出一個需求:編寫一個方法execAll,執行一次可以把所有匹配的結果捕獲到(前提,正則一定要設置全局修飾符g)
~function() { function execAll(str = '') {????//?進來后的第一件事,就是驗證當前正則是否設置了g,????//?不設置則不能再進行循環捕獲了,否則會導致死循環 if(!this.global) return this.exec(str); // => str:要匹配的字符串 // => this:RegExp的示例(當前操作的正則) // ary存儲最后所有捕獲的信息 reg存儲每一次捕獲的內容 let ary = []; let res = this.exec(str); while(res) {??????????ary.push(res[0]);???????????//?把每一次捕獲的內容存放到數組當中 // => 只要捕獲的內容不為null,則繼續捕獲下去 res = this.exec(str);????????} return ary.length === 0 ? null : ary; } RegExp.prototype.execAll = execAll;}();
使用:
// 加g:let str = "cos2019yangfan2020qihang2021";let reg = /\d+/g;reg.execAll(str);?//?['2019,'2020','2021']let str2 = 'hhh';let str3 = '12@13hhh'reg.execAll(str2);?//?nullreg.execAll(str3);?//?['12,'13']
// 不加g的情況下:let reg2 = /\d+/;reg2.execAll(str2);?//?nullreg2.execAll(str3);?//?["12",?index:?0,?input:?"12@13hhh",?groups:?undefined]
其實還有一種辦法,就是直接使用字符串的match方法:
str3.match(reg2); // ["12", "13"]// 字符串中的match方法,可以在執行一次的情況下,捕獲到所有匹配的數據(前提是正則必須加g才可以)
4-正則的分組捕獲:
身份證號碼:
let?str?=?'610521199003020017';let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;reg.test(str);?//?truereg.exec(str);?//?["610521199003020017",?"610521",?"1990",?"03",?"02",?"1",?"7",?index:?0,?input:?"610521199003020017",?groups:?undefined]str.match(reg);//?["610521199003020017",?"610521",?"1990",?"03",?"02",?"1",?"7",?index:?0,?input:?"610521199003020017",?groups:?undefined]
我們對上述正則不活的內容做一個分析:
????a.數組第一項是大正則匹配的結果
??? b.其余項:每一個小分組單獨匹配捕獲的結果
????c.如果設置了分組(改變優先級)但是捕獲的時候不需要單獨捕獲,可以基于?:(只匹配不捕獲)來處理
????d.比如我不想要最后一項(因為沒有用),我們不能去掉小括號,因為|會存在優先級的問題,所以我在最后一項的括號中添加一個 ?: 表示只匹配不捕獲(?:\d|X) 這個時候就不會捕獲到最后的一項了
5-分組捕獲
比如我們定義了一個字符串時間格式為:{2019}年{11}月{8}日
既要捕獲到{數字},也想單獨的把數字也獲取到,例如:第一次找到{0},還需要單獨獲取0
let str = "{2019}年{11}月{8}日";let reg = /\{\d+\}/; reg.exec(str);?//["{2019}",?index:?0,?input:?"{2019}年{11}月{8}日",?groups:?undefined]str.match(reg);?//["{2019}",?index:?0,?input:?"{2019}年{11}月{8}日",?groups:?undefined]
這種情況下只能捕獲到大正則不能捕獲小分組。????
那如何才能獲取到小分組呢:
// 加小括號,分組捕獲:let str = "{2019}年{11}月{8}日";let reg = /\{(\d+)\}/;reg.exec(str);?//?["{2019}",?"2019",?index:?0,?input:?"{2019}年{11}月{8}日",?groups:?undefined]str.match(reg);?//?["{2019}",?"2019",?index:?0,?input:?"{2019}年{11}月{8}日",?groups:?undefined]
分組捕獲,先匹配大正則,再把對應的小分組匹配到。
但問題又來了,如果我想多次匹配呢?
// 加小括號,加g:let str = "{2019}年{11}月{8}日";let reg = /\{(\d+)\}/g;reg.exec(str);?//?["{2019}",?"2019",?index:?0,?input:?"{2019}年{11}月{8}日",?groups:?undefined]str.match(reg);?//?["{2019}",?"{11}",?"{8}
稍微用心的同學肯定又發現了,多次匹配的情況下,match只能把大正則匹配的內容獲取到,小分組匹配的信息無法獲取。
臥槽?這……呵呵噠,怎么辦?針對這種match無法匹配小括號中的正則的情況,我自己寫了一個函數:
let str = "{2019}年{11}月{8}日";let reg = /\{(\d+)\}/g;let?arrBig?=?[],??//?大正則匹配的內容??arrSmall?=?[],??//?小分組匹配的內容 res = reg.exec(str); while(res) { let [big,small] = res; arrBig.push(big); arrSmall.push(small); res = reg.exec(str);}console.log(arrBig, arrSmall); // ["{2019}", "{11}", "{8}"] ["2019", "11", "8"]
6-正則分組引用,
一般如果看到兩個一樣,比如:book ,oo 一樣,那么就要考慮正則的分組引用,這里也就看到了分組的第三個作用:分組引用
let?str?=?'book';?// 類似?good?look?moon?foot 這種...let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; // 分組引用就是通過 "\數字" 讓其代表和對應分組出現一模一樣的內容reg.test(str);?//?=>truereg.test('str');?//?=>falsereg.test('week');?//?=>truereg.test('weeks');?//?=>false
7-正則捕獲的貪婪性:
let str = 'cos2019揚帆2020起航2021';let reg = /\d+/g;str.match(reg);?//?["2019",?"2020",?"2021"]//?我們發現:把所有跟正則匹配的東西都拿到了
我們發現上面的代碼 str.match(reg) 把所有跟正則匹配的東西都拿到了,也就是把像2019這樣的數字整個都拿到了,而不是分開一個一個拿到了 '2', '0', '1', '9' 這樣的單個數字。這是因為正則捕獲的貪婪性。
正則捕獲的貪婪性:默認情況下正則捕獲的時候是按照當前正則所匹配的最長結果來獲取的
這個是正則捕獲的貪婪性。
那么如果我們不希望這樣我們應該怎么做?
可以在量詞元字符后面設置??取消捕獲時候的貪婪性 (按照正則匹配的最短結果來獲取)
reg = /\d+?/g;str.match(reg); //["2", "0", "1", "9", "2", "0", "2", "0", "2", "0", "2", "1"]
*問號在正則中的5大作用:
??? 1- 問號左邊是非量詞元字符:本身代表量詞元字符,出現0-1次
??? 2- 問號左邊出是量詞元字符:取消捕獲時候的貪婪性
??? 3- (?:) 只匹配不捕獲
??? 4- (?=) 正向預查
??? 5- (?!) 負向預查
*小括號用于分組,分組的3個作用:
??? 1- 改變有優先級
??? 2- 分組捕獲
??? 3- 分組引用
四、其他正則捕獲的方法
1- test也能捕獲(本意是匹配)
RegExp.$1~RegExp.$9,獲取當前正則匹配后,第一個到第九個分組的信息
let?str?=?"{2020}年{2}月{28}日";let reg = /\{(\d+)\}/g;reg.test(str);?//?=>?trueRegExp.$1?//?=>?2020reg.test(str);?//?=>?trueRegExp.$1?//?=>?2reg.test(str);?//?=>?trueRegExp.$1?//?=>?28reg.test(str);?//?=>?falseRegExp.$1?//?=>?28??//?這里的28存儲的是上次捕獲的結果
2- replace 字符串中實現替換的方法(一般都是伴隨正則一起使用的)
let?str?=?'cos@2019|cos@2020';?//需求:把'cos' 替換成 'rose'// 1- 不用正則,執行一次只能替換一個str = str.replace('cos','rose'); console.log(str);//rose@2019|cos@2020 //2- 使用正則相對簡單:str = str.replace(/cos/g,'rose'); console.log(str); //rose@2019|rose@2020
3- 但是有些情況不使用正則是搞不定的,正則的優勢凸顯了吧,
//?比如把?cos?==>?cosplaylet str = 'cos@2019|cos@2020'; str = str.replace('cos','cosplay'); //cosplay@2019|cos@2020str = str.replace('cos','cosplay'); //cosplayplayplayplay@2019|cos@2020console.log(str);// 每次都0的位置開始,所以永遠替換掉的是最開始的,類似于正則捕獲的懶惰型
let str = 'cos@2019|cos@2020'; str = str.replace(/cos/g,'cosplay'); // replace不會改變原有字符串console.log(str); // cosplay@2019|cosplay@2020
你看,基于正則可以一次實現。
五、實例
看了這么多東西,是不是有點煩躁了。來吧,看幾個實例。
案例一:把時間字符串進行處理
let?time?=?'2019-11-12';// ==> 變為 2019年11月12日let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;// 這種方法是可以實現的:time = time.replace(reg,'$1年$2月$3日');console.log(time);?//2019年11月12日
還可以這樣處理:
語法, [str].replace([reg],[function])
a-首先拿 reg 和 time 進行正則匹配捕獲,能匹配幾次就會把傳遞的函數執行幾次(而且是匹配一次就執行一次)
b-不僅方法執行,而且 replace 還給方法傳遞了實參信息(其實就是和exec捕獲的內容一致的信息:大正則匹配的內容 和 小分組匹配的信息…)
c- 在函數中,我們返回的是什么,就會把當前大正則匹配的內容替換成什么
...time = time.replace(reg,() => { // ...});
案例二:對時間字符串進行處理:
let?time?=?"2019-1-15";//?=>?變為2019年1月15日let?reg?=?/^(\d{4})-(\d{1,2})-(\d{1,2})$/;
1- 這樣寫是沒有問題的
time = time.replace(reg,'$1年$2月$3日'); //字符串通過replace如果第一項放入的是正則,那么后面通過$1..可以獲取到分組中的信息console.log(time); //2019年11月15日
2- 還可以這樣寫 [str].replace([reg],[function])
a-首先拿reg 和 time進行匹配捕獲,能匹配幾次就會把傳遞的函數執行幾次(而且是匹配一次,就執行一次)
b-不僅把方法執行了,而且replace還給方法傳遞實參信息(和exec捕獲的內容一致的信息:大正則匹配的內容,小分組匹配的信息,...)
c- 函數中return的是什么,就是把當前大正則匹配結果替換成什么樣
time = "2019-1-15";time = time.replace(reg,(...arg) => { let [,$1,$2,$3] = arg; ??// 解構賦值第一項是大正則獲取的內容,因為我們不需要所以用?逗號?空出來 $2.length <2 ? $2 = '0' + $2 : null; $3.length <2 ? $3 = '0' + $3 : null; return $1+'年'+$2+'月'+$3+'日';}); console.log(time); // 2019年01月15日
案例三:單詞首字母大寫
let?str?=?'good?good?study,?day?day?up!';let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;// 函數被執行了6次,每次都把正則匹配的信息傳遞給函數str?=?str.replace(reg,?(...arg)?=>?{ let [content,$1] = arg; $1 = $1.toUpperCase(); content = content.substring(1) return $1 + content;});console.log(str); // Good Good Study, Day Day Up!
案例四:驗證一個字符串中哪個字母出現的次數最多,多少次?
let str = 'webwangchengbin';
A- 去重思維:
let?obj?=?{};[].forEach.call(str, char => { if (typeof obj[char] !== 'undefined') { obj[char] ++; return; } obj[char] = 1;});console.log(obj); // 通過設置一個最大值的方式let max = 1, res = [];for (let key in obj) { let item = obj[key]; item > max ? max = item : null;}console.log('max==>', max); // 3for (let key in obj) { let item = obj[key]; if (item == max) { res.push(key); }}console.log(`出現次數最多的字母是:${res},出現了${max}次`); //出現次數最多的字母是:n,出現了3次
B-排序:
letstr = 'webwangchengbin';str = str.split('').sort((a,b) => a.localeCompare(b)).join('');console.log(str); //abbceegghinnnwwlet reg= /([a-zA-Z])\1+/g;// a--如果只要第一個出現次數最多的字母,不考慮多個字母出現次數相同的情況。:let ary = str.match(reg);console.log(ary); //["bb", "ee", "gg", "nnn", "ww"]ary.sort( (a,b) => b.length - a.length);console.log(ary); // ["nnn", "bb", "ee", "gg", "ww"]console.log(`出現次數最多的字母是:${ary[0].substring(0,1)},出現了${ary[0].length}次`);// b-多個字母出現次數相同:let max = ary[0].length, res = [ary[0].substring(0,1)];for (let i=1; i let item = ary[i]; if (item.length < max) { break;??} res.push(item.substring(0,1));}console.log(`出現次數最多的字母是:${res},出現了${max}次`)
C-從最大到最小找:
let str = 'webwangchengbinnxxxx', max = 0, res = [], flag = false;str = str.split('').sort( (a, b) => a.localeCompare(b)).join('');console.log(str); //abbceegghinnnnwwxxxxfor (let i=str.length; i>0; i--) { // /([a-zA-Z])\1{i-1}/ 兩個斜杠之間的都是元字符,如果想把變量的值作為正則的一部分,我們只能用new RegExp() 這種方式 let reg = new RegExp('([a-zA-Z])\\1{'+(i-1)+'}','g'); // 在字符串中一個斜杠表示的是轉義的意思,兩個斜杠才是斜杠的意思 // ==> 上面的正則就是有一個字母,然后重復出現指定次數 str = str.replace(reg, (content,$1) => { res.push($1); max = i; flag = true; }); // 只要能夠進入這個函數,就應該結束循環,所以我們再定義一個變量 if (flag) break;}console.log(`出現次數最多的字母是:${res},出現了${max}次`); //出現次數最多的字母是:n,x,出現了4次
案例五:時間字符串格式化
比如:
從服務器獲取的格式:
????? '2019-11-19 15:39:5'
????? '2019/11/19 15:39:5'
想要轉化為:
????? 11月19日 15時39分
????? 2019年11月19日
~?function?()?{???// formatTime?時間字符串的格式化處理方法 function formatTime() { // 1-首先獲取事件字符串中的年月日等信息 let timeAry = this.match(/\d+/g); // 因為調用的時候是 time.formatTime()這種形式, 所以this就是我們的 formatTime let template = '{0}年{1}月{2}日 {3}時{4}分{5}秒'; template = template.replace(/\{(\d+)\}/g, (content, $1) => { // => content: 當前本次大正則匹配的信息 // => $1: 本次小分組單獨匹配的信息????????//?以$1的值作為索引,到timeAry中找到對應的時間????????let?time?=?timeAry[$1]?||?'00';?//?如果沒有時分秒?那么我們會得到undefined,這個時候我就用?00?表示????????time.length?2???time?=? return time; }); return template; } // 擴展到內置類 String.prototype上 如果擴展的比較多的話就這么寫,否則就按照正常的擴展就行了 ['formatTime'].forEach(item => { // String.prototype['formatTime'] = 'formatTime' // 右邊的不能是字符串必須是個方法才可以,所以用eval轉化一下 String.prototype[item] = eval(item); }); }();let time = '2019-11-19 15:39:5';console.log(time.formatTime()); //2019年11月19日 15時39分05秒
看起來很完美對嗎?但是現在輸出的格式是固定的,我們希望這個格式可以由用戶來控制。如果這個格式用戶沒有傳遞,那么我們就使用默認的這個固定格式。
~?function?()?{ /* @params: template: [string] 我們最后期望獲取的日期格式的模板 模板規則: {0}=>年 {1}=>月 {2}=>日 {3}=>時 {4}=>分 {5}=>秒 @return [string]格式化后的時間字符串 by WangYuxi on 2019/11/19 */ function formatTime(template = '{0}年{1}月{2}日 {3}時{4}分{5}秒') { let timeAry = this.match(/\d+/g); return template = template.replace(/\{(\d+)\}/g, (...[, $1]) => { let time = timeAry[$1] || '00'; return time.length < 2 ? '0' + time : time; }); } ['formatTime'].forEach(item => { String.prototype[item] = eval(item); });}();let time = '2019-11-19 15:39:5';console.log(time.formatTime()); //2019年11月19日 15時39分05秒console.log(time.formatTime('{1}月{2}日 {3}:{4}')); //11月19日 15:39console.log(time.formatTime('{1}-{2} {3}:{4}')); //11-19 15:39console.log(time.formatTime('{0}年{1}月{2}日')); //2019年11月19日time = '2019-11-19';console.log(time.formatTime()); //2019年11月19日 00時00分00秒
案例六:對url地址進行處理:
~function() { /* queryURLParams: 獲取URL地址問號后面參數的值,(可能也包含hash值) @params: @return: [object] 把所有參數以鍵值對的方式存儲起來并且返回 */ function queryURLParams() { let obj = {}; this.replace(/([^?=]+)=([^?=]+)/g, (...[, $1, $2]) => obj[$1] = $2); this.replace(/#([^?=]+)/g, (...[,$1]) => obj['HASH'] = $1); return obj; } ['formDate','queryURLParams'].forEach( item => { String.prototype[item] = eval(item); })}(); let time = '2019-11-19 19:22:3';console.log(time.formDate());let url = 'http://www.cos.cn/?lx=1&from=wx#video';// ==> {lx:1,form:'wx',hash:'video'}console.log(url.queryURLParams()); //{lx: "1", from: "wx", HASH: "video"}
案例七:千分符
A- 不用正則:把字符串倒過來處理
let num = '1123456789'; //'1,123,456,789' 千分符num = num.split('').reverse().join('');for (let i=2; i-1; i+= let prev = num.substring(0, i+1), next = num.substring(i+1); num = prev + ',' + next;}num = num.split('').reverse().join('');console.log('===>', num); // ===> 1,123,456,789
B-正則:
~function()?{ /* millimeter: 實現大數字的千分符處理 @params @return [string] 千分符后的字符串 */ function millimeter() { return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ','); } ['formDate', 'queryURLParams', 'millimeter'].forEach( item => { String.prototype[item] = eval(item); })}();let num = '1123456789'; //'1,123,456,789' 千分符console.log(num.millimeter());
吐血嗎?正則會這么多,可以滿足你日常工作和面試大部分的需求了。
相信能看到這里的人也很少。隨意隨意:-)