文章目錄
題目來源於牛客網前端專題:
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])
和 ^
。
^
表示開頭前面的位置【也就是開頭位置,因爲位置都是空字符串,n個位置疊加起來還是一個位置】。.*[0-9]
,表示有0~n個任意字符,後面再跟一個數字。【也就是數字位置是任意的,可以在密碼的開頭,也可以在中間】(?=.*[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 中的參數——原題
- 指定參數名稱,返回該參數的值 或者 空字符串
- 不指定參數名稱,返回全部的參數對象 或者 {}
- 如果存在多個同名參數,則返回數組
建立空對象存放結果,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 爲出現頻率
- 不限制 key 的順序
- 輸入的字符串參數不會爲空
- 忽略空白字符
/\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;
}
三、修改原字符串:
(結合
replece
,match
方法)
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)
- 以 - 爲分隔符,將第二個起的非空單詞首字母轉爲大寫
- -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——原題
- rgb 中每個 , 後面的空格數量不固定
- 十六進制表達式使用六位小寫字母
- 如果輸入不符合 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}},替換成值
思路
思路
- 使用
(/\{\{(.+?)\}\}/g
+replace
全局匹配字符串中{{}}的內容,注意要使用?實現惰性匹配,否則可能匹配{{xxx}}{{xxx}}中間的所有部分 - 回調函數參數:
m
是整個內容,s1
是括號內子表達式。 - 當
s1
爲data,而且傳入data類型是基礎類型
時,直接用值替代data - 當
s1
不爲data,split(".")
分割字符串(子串個數不確定時,只能使用split
),得到s1Key
數組,數組爲空
或者data不是對象
,不進行替換,也就是返回m
- 當
數組不空
而且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>"