regular expression : RegExp
用來處理字符串的規則
- 只能處理字符串
- 它是一個規則:可以驗證字符串是否符合某個規則(test),也可以把字符串中符合規則的內容捕獲到(exec/match...)
let str = "you can you up, no can no bb"
let reg = /\d+/
reg.test(str); // false
str = "123-456"
reg.exec(str); // ["123", index: 0, input: "123-456", groups: undefined]
編寫正則表達式
兩種創建方式
// 字面量方式
let reg = /\d+/;
// 構造函數模式創建
let reg = new RegExp("\\d+");
兩者區別:
1.構造函數中傳遞的是字符串,所以\
需要寫兩個才能代表斜杠
2.兩個斜杠中間包起來的都是元字符,如果正則中要包含某個變量的值,則不能用字面量創建的方式創建,而應轉用構造函數模式
let name = "leonard";
reg = /^@"+name+"@$/;
reg.test("@leonard@"); // false
reg.test('@""""nameeeee"@'); // true
reg = new RegExp("^@"+name+"@$");
reg.test("@leonard@"); // true
正則表達式由兩部分組成
- 元字符
- 修飾符
/* 常用元字符 */
// 量詞元字符:設置出現的次數
* 零到多次
+ 一到多次
?零次或一次
{n} 出現n次
{n,} 出現n到多次
{n,m} 出現n到m次
// 特殊元字符:單個或者組合在一起代表特殊的含義
\ 轉義字符
. 除\n外的任意字符
^ 以哪一個元字符作為開始
$ 以哪一個元字符作為結束
\n 換行符
\d 0~9之間的一個數字
\D 非0~9之間的一個數字
\w 數字、字母、下劃線中的任意一個字符
\s 一個空白字符(包含空格、制表符、換頁符等)
\t 一個制表符(一個TAB鍵:四個空格)
\b 匹配一個單詞的邊界
x | y x或者y中的一個字符
[xyz] x或者y或者z中的一個字符
[^xy] 除了x/y以外的任意字符
[a-z] 指定a-z這個范圍中的任意字符
[^a-z] 除了a-z這個范圍外的任意字符
() 正則中的分組符號
(?:) 只匹配不捕獲
(?=) 正向預查
(?!) 負向預查
// 普通元字符:代表本身含義的
/leonard/ 匹配的就是字符串"leonard"
/* 常用修飾符 : img */
i => ignoreCase 忽略單詞大小寫匹配
m => multiline 可以進行多行匹配
g => global 全局匹配
元字符詳解
-
^
和$
let reg = /^\d/;
reg.test("leonard"); // false
reg.test("2333leonard"); // true
reg.test("leonard2333"); // false
let reg = /\d$/;
reg.test("leonard"); // false
reg.test("2333leonard"); // false
reg.test("leonard2333"); // true
let reg = /^1\d{10}$/;
reg.test("1234567890"); // false
\
let reg = /^2.3$/;
let reg1 = /^2\.3$/;
reg.test("2.3"); // true
reg.test("2@3"); // true
reg1.test("2.3"); // true
reg1.test("2@3"); // false
x | y
let reg = /^(18|29)$/ // ()處理優先級問題
reg.test("18"); // true
reg.test("18"); // true
reg.test("129"); // false
reg.test("189"); // false
[]
let reg = /^[@+]$/; // 中括號中出現的字符一般都代表本身的含義
reg.test("@@"); // false
reg.test("@+"); // false
reg.test("@"); // true
reg = /^[\d]$/; // \d在中括號中還是0-9
reg.test("d"); // false
reg.test("\\"); // false
reg.test("9"); // true
reg = /^[10-29]$/; // 中括號中不存在多位數
reg.test("1"); // true
reg.test("9"); // true
reg.test("0"); // true
reg.test("10"); // false
常用的正則表達式
1.驗證是否為有效數字
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
2.驗證6~16密碼,含數字、字母、下劃線
let reg = /^\w{6,16}$/
3.驗證真實姓名(2~10位)
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
4.驗證郵箱
let reg = /^\w+((-\w+)|(\.\w+))*@[a-zA-Z0-9]+((\.|-)[a-zA-Z0-9]+)*\.[a-zA-Z0-9]+$/
5.身份證號碼
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/
正則的捕獲
實現正則捕獲的方法
-
RegExp.prototype
上的方法-
exec
基于exec實現正則的捕獲
1.捕獲到的結果是null或者一個數組
首項:本次捕獲到的內容
其余項:對應小組本次單獨捕獲的內容
index:當前捕獲內容在字符串中的起始索引
input:原始字符串
2.每執行一次exec只能捕獲到一個符合正則規則的
正則捕獲的懶惰性:由于默認情況下lastIndex的值不會被修改,所以每次執行只捕獲第一個
解決辦法:全局修飾符g,全部捕獲后會出現循環捕獲現象
-
let str = "leonard2333age18"
let reg = /\d+/;
reg.exec(str); // ["2333" ...]
reg.exec(str); // ["2333" ...]
reg.exec(str); // ["2333" ...]
let reg1 = /\d+/g;
reg1.exec(str); // ["2333" ...]
reg1.exec(str); // ["18" ...]
reg1.exec(str); // null
reg1.exec(str); // ["2333" ...]
test
let str = "{0}年{1}月{2}日";
let reg = /\{(\d+)\}/g;
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 0
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 1
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 2
console.log(reg.test(str)); // false
console.log(RegExp.$1); // 2,存儲上一次捕獲的結果
- 字符串
String.prototype
上支持正則表達式處理的方法replace
let time = "2019-8-8";
let reg = /(\d{4})-(\d{1,2})-(\d{1,2})/g;
console.log(time.replace(reg,"$1年$2月$3日")); // "2019年8月8日"
time = time.replace(reg,(...args)=>{
let [,$1,$2,$3] = args;
return `${$1}年${$2}月${$3}日` // 返回內容替換匹配內容
})
console.log(time); // "2019年8月8日"
// 利用replace實現單詞首字母大寫
let str = "how are you?";
let reg1 = /\b([a-zA-Z])[a-zA-Z]*\b/g;
str = str.replace(reg1, (...args)=>{
let [content, $1] = args;
$1 = $1.toUpperCase();
content = content.substring(1);
return $1 + content;
})
console.log(str); // "How Are You?"
match
let str = "leonard2333leonard123leonard333";
let reg = /\d+/g;
console.log(str.match(reg)); // ["2333", "123", "333"]
splite
基于exec實現一次全匹配的方法execAll
~ function(){
function execAll(str = ""){
if(!this.global) return this.exec(str);
let ary = [],
res = this.exec(str);
while(res){
ary.push(res[0]);
res = this.exec(str);
}
return ary.length === 0 ? null : ary;
}
RegExp.prototype.execAll = execAll;
}()
let str = "leonard2333leonard123leonard333";
let reg = /\d+/g;
console.log(reg.execAll(str)); // ["2333", "123", "333"]
分組捕獲
let str = "350623198401234423";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
let reg1 = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/; // 只匹配不捕獲
reg.exec(str);
str.match(reg);
// ["350623198401234423", "350623", "1984", "01", "23", "2", "3"...]
reg1.exec(str);
str.match(reg1);
// ["350623198401234423", "350623", "1984", "01", "23", "2"...]
分組引用
let str = "tooth";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z][a-zA-Z]$/;
console.log(reg.test(str)); // true
正則捕獲的貪婪性
let str = "啊嘍哈2333@3222";
// 正則捕獲的貪婪性:默認情況下,正則捕獲的時候,是按照當前正則所匹配的最長結果來獲取的
let reg = /\d+/g;
console.log(str.match(reg)); // ["2333","3222"]
// 在量詞元字符后面設置?:取消捕獲的貪婪性
reg = /\d+?/g;
console.log(str.match(reg)); // ["2", "3", "3", "3", "3", "2", "2", "2"]
問號在正則中的五大作用
- 問號左邊是非量詞元字符:代表量詞元字符出現零到一次
- 問號左邊是量詞元字符:取消捕獲時的貪婪性
- (?:) 只匹配不捕獲
- (?=) 正向預查
- (?!) 負向預查
正向預查和負向預查
-
(?=pattern)
正向預查
在任何匹配pattern的字符串開始處匹配字符串,是非獲取字符串(即只匹配不獲?。?/li>
var str = "leonard111leonard2333leonard233leonard666"
var reg = /leonard(?=23+)/g;
var a = str.match(reg);
console.log(a); // ["leonard", "leonard"]
-
(?!pattern)
負向預查
匹配方式與正向相反,返回的是匹配不到的字符串,也是非獲取字符串
var str = "leonard111leonard2333leonard233leonard666"
var reg = /leonard(?!1+)/g;
var a = str.match(reg);
console.log(a); // ["leonard", "leonard", "leonard"]
- 曾經面過的一道題
驗證6-16位的密碼,可以是數字、字母、下劃線,但要求必須包含大小寫字母、數字三項
正向預查寫法:
let reg = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])\w{6,16}$/g
負向預查寫法:
let reg = /^(?![0-9]+$)(?![A-Z]+$)(?![a-z]+$)(?![0-9a-z]+$)(?![0-9A-Z]+$)(?![a-zA-Z]+$)\w{6,16}$/g
面試題
獲取出現次數最多的字母
// Method 1
let str = "let me put it this way";
let obj = {};
[].slice.call(str).forEach(item=>{
if(item === ' ') return;
if(typeof obj[item] !== 'undefined'){
obj[item]++;
return;
}
obj[item] = 1;
})
let max = 1,
res = [];
for(let key in obj){
max < obj[key] ? max = obj[key] : null;
}
for(let key in obj){
max === obj[key] ? res.push(key) : null;
}
console.log(`出現最多次的字母是${res.join()},總共出現了${max}次`)
// Method 2
let str = "let me put it this way";
let obj = {};
str = str.replace(/\s/g,() => '').split('').sort((a,b) => a.localeCompare(b)).join('');
let reg = /([a-zA-Z])\1*/g;
let ary = str.match(reg).sort((a,b) => b.length - a.length);
let res = ary.slice(0,1);
let max = res[0].length;
for(let i = 1; i < ary.length; i++){
if(ary[0].length !== ary[i].length) break;
res.push(ary[i])
}
res = res.map((item) => {
return item.substr(0,1);
})
console.log(`出現最多次的字母是${res.join()},總共出現了${max}次`);