【前端js】算法全歸納(三)字符串:最常用的20個正則表達式(附實例詳解)


題目來源於牛客網前端專題:
https://www.nowcoder.com/ta/front-end?page=1
https://www.nowcoder.com/ta/js-assessment
https://www.nowcoder.com/ta/coding-interviews
其他常用正則總結https://www.jianshu.com/p/4fb6354708e6


一、表單驗證常用

(結合test方法)
一般驗證輸入是否符合標準,需要加上^$匹配整個字符串

1.顏色

/^rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/ rgb(255, 255, 255)格式的10進制顏色,可以帶空格
/^#?(\w{6}|\w{3})$/十六進制顏色,字符可以出現 3 或 6 次,需要是用量詞和分支結構。

2.用戶名

/^\w{4,16}$/ 4到16位(字母,數字,下劃線,減號)

3.時間

/^[01]\d|[2][0-3]:[0-5]\d$/ 24 小時制,0/1和2開頭的情況分支。共 4 位數字,第一位數字可以爲 [0-2]。 當第 1 位爲 “2” 時,第 2 位可以爲 [0-3],其他情況時,第 2 位爲 [0-9]。 第 3 位數字爲 [0-5],第4位爲 [0-9]。
/^(0?\d|1\d|2[0-3]):(0?\d|[1-5]\d)$/前面的 “0” 可以省略。時鐘0,1,2開頭分支,分鐘0和1-5開頭分支

4.日期

/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/年份四位數字,月份0(不可爲00),1分支,日期0(不可爲00),12(0-9),3(0-1)分支
/\d{4}(-|\/|\.)\d{2}\1\d{2}/連接符號可以是. - /的日期,反向應用綁定前面的獲取

5.驗證密碼問題

/(?=.*\d)(?=.*[a-z])^\w{6,12}$/ 密碼長度 6-12 位,由數字、小寫字符和大寫字母,下劃線組成,但必須包括數字和英文。
先試驗(?=.*[0-9])(?=.*[a-z])\b\w{6,12}\b,發現確實只匹配字母和數字的結合的密碼:

在這裏插入圖片描述
具體原理:
只需要弄明白 (?=.*[0-9])^ 即可。分開來看就是 (?=.*[0-9])^

  1. ^表示開頭前面的位置【也就是開頭位置,因爲位置都是空字符串,n個位置疊加起來還是一個位置】。
  2. .*[0-9],表示有0~n個任意字符,後面再跟一個數字。【也就是數字位置是任意的,可以在密碼的開頭,也可以在中間】
  3. (?=.*[0-9])^ 表示在字符串開頭位置的後面(也就是整個字符串中),必須要有.*[0-9]——一個任意位置的數字,也就是這串密碼必須有數字。

6.郵箱字符串判斷

郵箱前面的域名可能沒有.xxx,數量爲0-n;後面的域名格式一定爲xxx.xxx,.xxx數量爲1-n

function isAvailableEmail(sEmail) {
    if(sEmail.length<5){
        return false;
    }
    return /^\w+(\.\w+)*@\w+(\.\w+)+$/.test(sEmail);
    
}

7.身份證

/^(\d{15}|\d{17}[\dxX])$/
判斷是否包含數字

var num =/\d/;
return num.test(str);

8.不匹配任何東西

/.^/

9.檢查重複字符串

給定字符串 str,檢查其是否包含連續重複的字母(a-zA-Z),包含返回 true,否則返回 false
只能通過反向引用來檢查

function containsRepeatingLetter(str) {
    if(str.length<=1){
        return false;
    }
    return /([a-zA-Z])\1/.test(str);

}

10.檢查重複單詞

\b(\w+)\b\s+\1\b

11.判斷是否以元音字母結尾

給定字符串 str,檢查其是否以元音字母結尾
1、元音字母包括 a,e,i,o,u,以及對應的大寫
2、包含返回 true,否則返回 false
注意如果用分支,需要用括號,否則$會跟隨最後一個分支

function endsWithVowel(str) {
    if(str===""){
        return false;
    }
    return /[aeiouAEIOU]$/.test(str)

}

12.判斷字符串是否爲XXX-XXX-XXXX格式

function matchesPattern(str) {
    if(str.length<12){
        return false;
    }
    return /^\d{3}-\d{3}-\d{4}$/.test(str);  

}

13.判斷是否符合 USD 格式

給定字符串 str,檢查其是否符合美元書寫格式
1、以 $ 開始
2、整數部分,從個位起,滿 3 個數字用 , 分隔
3、如果爲小數,則小數部分長度爲 2
4、正確的格式如:$1,023,032.03 或者 $2.03,錯誤的格式如:$3,432,12.12 或者 $34,344.3

function isUSD(str) {
    if(str.length<=1){
        return false;
    }
    return /^\$[1-9]\d{0,2}(,\d{3})*(\.\d{2})?$/.test(str);

}

$,.需要轉義
第一部分:[1-9]\d{0,2},首位不能爲0,可能爲1-3位
第二部分:(,\d{3})*,逗號➕三位數字,可能出現0-n次
第三部分:(\.\d{2})?,小數點➕小數點後兩位,可能出現0-1次

14.模擬一個正則表達式匹配函數test

題目描述 https://www.nowcoder.com/practice/6f8c901d091949a5837e24bb82a731f2?tpId=13&tqId=11206&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示數值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
正則表達式分成四部分:[+-]? 匹配符號,\d* 匹配小數點前的數字,(\.\d+)?匹配小數點後的數字,([eE][+-]?\d+)?e和e部分以後的指數。

function isNumeric(s) {
  // write code here
  return /^[+-]?\d*(\.\d+)?([eE][+-]?\d+)?$/.test(s);
}

二、獲取匹配/子表達式:

(結合match,replece方法) 當需要獲取子表達式的時候:
不需要g全局,只匹配一次,使用match獲取數組第1位以後的內容;
需要g全局,匹配多次,使用replece+函數迭代操作子表達式;

1.url

(/\??(\w+)=(\w+)&?/g匹配參數對,/??表示儘可能少匹配/,可能出現0~1次
獲取 url 中的參數——原題

  1. 指定參數名稱,返回該參數的值 或者 空字符串
  2. 不指定參數名稱,返回全部的參數對象 或者 {}
  3. 如果存在多個同名參數,則返回數組
    建立空對象存放結果,replace遍歷匹配結果,對子表達式進行操作,判斷對象鍵值是否已經存在,不存在則存入value,存在則數組拼接新value,最後根據輸入的Key查詢對象裏的value,如果查詢得到undefined,通過或操作符指定此時返回結果爲空字符串:result[sKey] || ""
function getUrlParam(sUrl, sKey) {
  result = {};
  sUrl.replace(/\??(\w+)=(\w+)&?/g, function(str, key, value) {
    if (result[key] !== undefined) {
      var t = result[key];
      result[key] = [].concat(t, value);
    } else {
      result[key] = value;
    }
  });

  if (sKey === undefined) {
    return result;
  } else {
    return result[sKey] || "";
  }
}

2.ip地址

規則:

  • IP地址的長度爲32位(共有2^32個IP地址),分爲4段,每段8位

  • 用十進制數字表示,每段數字範圍爲0~255,

  • 段與段之間用句點隔開。
    // 0.0.0.0 ~ 255.255.255.255

  • 25[0-5]|2[0-4]\d匹配250~255, 200-240
    1\d{2}匹配100~199
    [1-9]?\d)匹配十位數+個位數

function testIp(str){
    return /^((2(5[0-5]|[0-4]\d))|1\d{2}|[1-9]?\d)(\.((2(5[0-5]|[0-4]\d))|1\d{2}|[1-9]?\d)){3}$/.test(str);
}

2. id

/id=".*?"/
惰性匹配是防止匹配最後的引號,因爲 . 是通配符,本身就匹配雙引號的,而量詞 * 又是貪婪的,當遇到 container 後面雙引號時,是不會 停下來,會繼續匹配,直到遇到最後一個雙引號爲止id="container" class="main"。加上變成惰 性匹配,匹配到第一個雙引號就結束了。

var regex = /id=".*"/ 
var string = '<div id="container" class="main"></div>'; console.log(string.match(regex)[0]); // => id="container" class="main"

3.html標籤

匹配一個開標籤,可以使用正則 <[^>]+>
匹配一個閉標籤,可以使用 <\/[^>]+>
但是要求匹配成對標籤,那就需要使用反向引用<\/\1>,/需要轉義,(.)*?惰性匹配

var regex = /<([^>]+)>(.)*?<\/\1>/g;
var string ="<title>regular expression</title>  <p>laoyao bye bye</p> <title>wrong!</p>"
string.match(regex)
//["<title>regular expression</title>", "<p>laoyao bye bye</p>"]

4.獲取連續字符串

給定字符串 str,檢查其是否包含 連續3個數字
1、如果包含,返回最新出現的 3 個數字的字符串
2、如果不包含,返回 false
僅匹配一次時,match數組【0】返回第一次匹配結果

function captureThreeNumbers(str) {
    if(str.length<=2){
        return false;
    }
    return str.match(/\d{3}/)?str.match(/\d{3}/)[0]:false;

}

5.匹配漢字

如果第二個參數 bUnicode255For1 === true,則所有字符長度爲 1,否則如果字符 Unicode 編碼 > 255 則長度爲 2——原題
Unicode 編碼 > 255 :/[\u0256-\uffff]/也就是漢字
如果bUnicode255For1爲true,長度就是s.length
如果bUnicode255For1爲false,/g全局搜索漢字,長度就是s.length+match的長度

function strLength(s, bUnicode255For1) {
    return s.length+(bUnicode255For1?0:(s.match(/[\u0256-\uffff]/g)||[]).length);
}
strLength('hello world, 牛客', false);//17

6.忽略空格的字符數量統計

統計字符串中每個字符的出現頻率,返回一個 Object,key 爲統計字符,value 爲出現頻率

  1. 不限制 key 的順序
  2. 輸入的字符串參數不會爲空
  3. 忽略空白字符
    /\S/g全局匹配任意非空格的字符,然後對所有匹配到的字符做對象鍵值存儲
function count(str) {
    if(str.length===1){
        return {str:1}
    }
  var obj = {};
  str.replace(/\S/g, function(m) {
    obj[m] ? obj[m]++ : (obj[m] = 1);
  });
  return obj;
}

三、修改原字符串:

(結合replecematch方法)

1.數字千分位

\B(?=(\d{3})+$) 數字轉換成千位分隔符表示法,比如把 “12345678”,變成 “12,345,678”。注意一定要帶$,他匹配的是距離字符結束的倒數第三個,第六個,第九個數字前面的位置.
\B限制了這個,只能出現在非單詞邊界的地方,也就是不會出現在開頭

var result = "012345678".replace(/(?=(\d{3})+$)/g, ',') 
console.log(result);//,012,345,678
var result = "012345678".replace(/\B(?=(\d{3})+$)/g, ',') 
console.log(result);//012,345,678

/\B(?=(\d{3})+\b)/g 有多個數字的千分位分隔:把裏面的結尾 $,修改成 \b

2.字符串 trim 去除空格模擬

trim 方法是去掉字符串的開頭和結尾的空白符。現在大部分瀏覽器中基本上都支持字符串的 trim 函數,

var str = " abcd ".trim();
//“abcd”

如果瀏覽器不兼容,有兩種思路去做。

  • 第一種,匹配到開頭和結尾的空白符,然後用replace的替換功能替換成空字符。
function trim(str) { 
return str.replace(/^\s+|\s+$/g, ''); 
} 
console.log( trim(" foobar ") ); // => "foobar"
  • 第二種,通過括號分組捕獲整個字符串,然後用replace的引用來生成需要的字符竄:
function trim (str) { 
return str.replace(/^\s*(.*?)\s*$/g, "$1"); 
} 
console.log( trim(" foobar ") ); // => "foobar"

這裏使用了惰性匹配 *?,不然.*也會匹配最後一個空格之前的所有空格的。

3.將每個單詞的首字母轉換爲大寫

\b匹配開頭/單詞邊界的位置,\w表示第一個字母

function titleize (str) { 
return str.toLowerCase().replace(/\b\w/g, function (c) { 
return c.toUpperCase(); }); 
} 
console.log( titleize('my name is epeli') ); 
// => "My Name Is Epeli"

\b不用(^|\s),是因爲\s是一個字符匹配符號,不能和位置匹配符號^並行,這樣會把空格\s也匹配進去:
在這裏插入圖片描述

4.連字符大駝峯化

/[-_\s]+\b(\w)/表示匹配的字符串:-m,-t,使用子表達式獲取(\w)首字母,然後轉化成大寫M,replace回調函數return這個大寫字母,就是使用這個大寫字母M(return結果)替代了-m(匹配,而非子表達式)。

function camelize (str) { 
return str.replace(/[-_\s]+\b(\w)/g ,function (match,c) { 
return c.toUpperCase();
}); 
} 
console.log( camelize('-moz-transform') ); 
// => "MozTransform"

5.連字符小駝峯化

[查看原題](https://www.nowcoder.com/practice/2ded24e34ec34325a62d42d0c8479bae?tpId=2&tqId=10861&tPage=1&rp=1&ru=/ta/front-end&qru=/ta/front-end/question-ranking)

  1. 以 - 爲分隔符,將第二個起的非空單詞首字母轉爲大寫
  2. -webkit-border-image 轉換後的結果爲 webkitBorderImage
    思路:
  • 考慮到第一個單詞首字母可能也是-w的形式,無法和後面的單詞首字母區分 ,所以只能先全部匹配,大駝峯化
  • 然後匹配第一個大寫字母,不加全局符號,把匹配結果替換成小寫,
function cssStyle2DomStyle(sName) {
    if(sName.length<=1){
        return sName;
    }
    return sName.replace(/-?\b(\w)/g,function(m,c){
        return c.toUpperCase();
    }).replace(/[A-Z]/,function(m){
        return m.toLowerCase();
    })

}
// => "MozTransform"

6.駝峯轉化成連字符

替換所有大寫字母成連字符+小寫字母

let str = 'MozTransform';
str.replace(/([A-Z])/g,(c)=>{return `-${c.toLowerCase()}`})
// => "-moz-transform"

8.字母數字交換順序

先捕獲字母和數字,注意(\d+)加號在括號裏面
然後"$2$1"直接交換

str.replace(/([a-zA-z]+)(\d+)/g, "$2$1");

9.時間格式化輸出

牛客網-原題
按所給的時間格式輸出指定的時間
格式說明
對於 2014.09.05 13:14:20
yyyy: 年份,2014
yy: 年份,14
MM: 月份,補滿兩位,09
M: 月份, 9
dd: 日期,補滿兩位,05
d: 日期, 5
HH: 24制小時,補滿兩位,13
H: 24制小時,13
hh: 12制小時,補滿兩位,01
h: 12制小時,1
mm: 分鐘,補滿兩位,14
m: 分鐘,14
ss: 秒,補滿兩位,20
s: 秒,20
w: 星期,爲 [‘日’, ‘一’, ‘二’, ‘三’, ‘四’, ‘五’, ‘六’] 中的某一個,本 demo 結果爲 五
使用正則表達式來replace來多次替換:

function formatDate(d, format) {
  var year = d.getFullYear(),
    month = d.getMonth() + 1,
    date = d.getDate(),
    hour = d.getHours(),
    minute = d.getMinutes(),
    second = d.getSeconds(),
    day = d.getDay(),
    week = ["日", "一", "二", "三", "四", "五", "六"];
  return format
    .replace(/yyyy/, year)
    .replace(/yy/, String(year % 100).padStart(2, "0"))
    .replace(/MM/, String(month).padStart(2, "0"))
    .replace(/M/, month)
    .replace(/dd/, String(date).padStart(2, "0"))
    .replace(/d/, date)
    .replace(/HH/, String(date).padStart(2, "0"))
    .replace(/H/, hour)
    .replace(/hh/, String(hour).padStart(2, "0"))
    .replace(/h/, hour % 12)
    .replace(/mm/, String(hour % 12).padStart(2, "0"))
    .replace(/m/, minute)
    .replace(/ss/, String(second).padStart(2, "0"))
    .replace(/s/, second)
    .replace(/w/, week[day]);
}

也可以考慮replace結合switch方法:https://www.jianshu.com/p/724e85c0941f
ES6 引入了字符串補全長度的功能,如果某個字符串不夠指定長度,會在頭部活尾部補全。padStart() 用於頭部補全;padEnd() 用於尾部補全。

padStart 和 padEnd 一共接受2個參數,第一個是用來指定字符串的最小長度,第二個參數是用來補全長度的字符串。
https://blog.csdn.net/ww430430/article/details/78363026

10.顏色rgb和16進制轉換

將 rgb 顏色字符串轉換爲十六進制的形式,如 rgb(255, 255, 255) 轉爲 #ffffff——原題

  1. rgb 中每個 , 後面的空格數量不固定
  2. 十六進制表達式使用六位小寫字母
  3. 如果輸入不符合 rgb 格式,返回原始輸入

思路:

  • 先match匹配正則,然後判斷返回結果是否爲null檢驗是否符合正則匹配,不符合直接return輸入值;
  • 然後對match的匹配數組遍歷,遍歷1-3的位置,這裏存儲的是匹配到的子表達式
  • 將子表達式轉化成整數,然後判斷是否大於16,進行補0操作,然後拼接字符串
  • 如果子表達式大於255,則格式不正確,直接return輸入值

注意這裏不能用replace操作,因爲replace適用於全局匹配,或者非全局匹配時將n個子表達式一起傳入一個回調函數進行操作;而這裏要求一次匹配同時對n個子表達式都進行同樣的操作,所以遍歷match的返回數組更合適

function rgb2hex(sRGB) {
    var rgb = /rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/;
    var rgbArr = sRGB.match(rgb);//rgbArr = {rgb(255, 255, 255),255,255,255}
    var str = "#";  //顏色值
    if(!rgbArr){  //不符合正則匹配
        return sRGB;
    }
    
        for(var i = 1 ; i<4; i++){
            var rgbIndex = parseInt(rgbArr[i]);
            if(rgbIndex<16 && rgbIndex>=0){
                str += ('0'+rgbIndex.toString(16));//保留兩位數
            }
            else if(rgbIndex>=16 && rgbIndex<=255){
                str += rgbIndex.toString(16);
            }
            else {
                return sRGB;  //不是顏色值字符
            }
        }
        return str;
    
}

11.正則表達式模版字符串

實現一個模版字符串:傳入一個數據(對象/基礎類型),和一段字符串格式的html模版,含有{{somedata}}/{{somedata.xxx}},替換成值
思路
思路

  1. 使用(/\{\{(.+?)\}\}/g+replace全局匹配字符串中{{}}的內容,注意要使用?實現惰性匹配,否則可能匹配{{xxx}}{{xxx}}中間的所有部分
  2. 回調函數參數:m是整個內容,s1是括號內子表達式。
  3. s1爲data,而且傳入data類型是基礎類型時,直接用值替代data
  4. s1不爲data,split(".")分割字符串(子串個數不確定時,只能使用split),得到s1Key數組,數組爲空或者data不是對象,不進行替換,也就是返回m
  5. 數組不空而且data是對象時,遍歷s1Key數組,對data遞歸查詢要訪問的value,最後替換爲查詢的值
function templateStr(data, str) {
  // 匹配字符串中{{}}的內容,m是整個匹配,s1是括號內子表達式
  return str.replace(/\{\{(.+?)\}\}/g, (m, s1) => {
    //s1是data而且data爲基礎類型,直接用值替代data
    if (s1 === "data" && !(data instanceof Object)) return data;
    //s1Key保存字符串分割以後的key
    let s1Key = s1.split(".");
    //s1Key是空或者data不是對象,返回匹配的m,不替換
    if (!s1Key || !(data instanceof Object)) return m;
    //s1Key不空而且data是對象
    let i = 1; //i指向當前key的索引
    let value = data; //value指向當前的obj.key
    while (value[s1Key[i]]) {
      //obj.key存在時,
      value = value[s1Key[i]]; //替換value
      i++; //指針後移
    }
    return value; //obj.key不存在,查詢完畢,用value替換匹配的m
  });
}
templateStr({ a: 1, b: { c: 2 } }, "<a href={{data.a}}>{{data.b.c}}</a>");
//"<a href=1>2</a>"
templateStr(5, "<a href={{data}}>{{data.b.c}}</a>");
//"<a href=5>{{data.b.c}}</a>"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章