JS正則表達式

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}次`);

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容

  • 正則表達式功能: 測試字符串的某個模式。例如對一個輸入字符串進行測試,看在該字符串是否存在一個電話號碼模式或一個信...
    DecadeHeart閱讀 615評論 0 1
  • @(javascript)[js正則表達式] [toc] JS正則表達式 正則表達式一直是一個令人頭疼但卻又是十分...
    踏浪free閱讀 435評論 0 1
  • 一、匹配符 \:指出接著的字符為特殊字符。 ^:表示匹配的字符必須在最前邊。 $:表示匹配的字符必須在最后邊。 *...
    活叁黃輝馮閱讀 788評論 0 3
  • RegExp對象實例化 RegExp是JS的正則表達式對象,實例化一個RegExp對象有字面量和構造函數2種方式。...
    octcms閱讀 2,014評論 0 3
  • js正則表達式(第一周) 從本周開始我將按時發表我的日志,每周一篇原創,日志內容主要是計算機知識,偶爾也會有電學內...
    發揮不廣泛閱讀 566評論 0 1