最近工作需要對諸如手機號碼進行脫敏,正好鞏固了一下正則表達式的使用,寫下文章記錄一下。
什麼是正則表達式
引用一下MDN上面的註解:
正則表達式是用於匹配字符串中字符組合的模式。在 JavaScript中,正則表達式也是對象。這些模式被用於 RegExp 的 exec 和 test 方法, 以及 String 的 match、matchAll、replace、search 和 split 方法。本章介紹 JavaScript 正則表達式。
編寫正則表達式
按照上文的解釋,我們有兩種方法來創建正則對象:
- 對象字面量:
var reg = /123/
- 通過原生RegExp對象創建:
var reg = new RegExp('123')
特殊字符模式
正則表達式是有簡單字符和特殊字符組成的,比方說1, 2, 3, ‘a’, 'b’這種是簡單字符;像\d, \w, \s這些是特殊字符。關於特殊字符的含義我就不說了,以下列出常用幾個,具體的可以去查文檔:MDN 正則表達式
- \d: 數字,比方說[0 - 9]
- \D: 非數字,比方說[^ 0 - 9]
- \w: 單字字符(數字,字母,下劃線),如:[a-zA-Z_0-9]
- \W: 匹配一個非單字字符。等價於 [^A-Za-z0-9_]。
- \s:匹配一個空白字符,包括空格、製表符、換頁符和換行符。
- \S: 匹配一個非空白字符。
相信大家發現一些規則,小寫字母都是匹配相應值,對應的大寫字母就在原基礎上取非。怎麼樣,很好記吧;再來幾個日常用到的關於個數匹配的解釋:
- {n}: n 是一個正整數,匹配了前面一個字符剛好出現了 n 次。
- {n,}: n是一個正整數,匹配前一個字符至少出現了n次。
- {n,m}: n 和 m 都是整數。匹配前面的字符至少n次,最多m次。如果 n 或者 m 的值是0, 這個值被忽略。
- [abc]: 一個字符集合。匹配方括號中的任意字符,包括轉義序列。
例子
話不多說了,正式開始寫例子了:
- 電話號碼脫敏處理。比如說:輸入:13580378064 輸出:135****8064
// 咱們先來一把常規方法處理
function decodeMobile(num) {
let numStr = num.toString()
if (!numStr || numStr.length < 11) return num
return numStr.substring(0, 3) + '****' + numStr.substring(7)
}
言歸正傳,上正餐:
第一版
function decodeMobile(num) {
let numStr = num.toString()
if (!numStr || numStr.length < 11) return num
return numStr.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}
好,這個正則出現特殊字符 () $1 $2都是幹嘛的呢,我們來看看:
引用一下MDN關於上面三個特殊字符的解釋:
(x) : 像下面的例子展示的那樣,它會匹配 ‘x’ 並且記住匹配項。其中括號被稱爲捕獲括號。
模式 /(foo) (bar) \1 \2/ 中的 ‘(foo)’ 和 ‘(bar)’ 匹配並記住字符串 “foo bar foo bar” 中前兩個單詞。模式中的 \1 和 \2 表示第一個和第二個被捕獲括號匹配的子字符串,即 foo 和 bar,匹配了原字符串中的後兩個單詞。注意 \1、\2、…、\n 是用在正則表達式的匹配環節,詳情可以參閱後文的 \n 條目。而在正則表達式的替換環節,則要使用像 $1、n 這樣的語法,例如,‘bar foo’.replace(/(…) (…)/, '$2 & 表示整個用於匹配的原字符串。
也就是說,(\d{3}) 會捕獲手機號的前三位數字,並且記錄匹配項,然後用$1 存起來,後續替換掉 。
但是第一版還有個問題,就是沒有校驗輸入的手機號是不是數字,如果是別的字符顯然不符合要求,接下來我們寫第二版:
第二版
function decodeMobile(num) {
let reg = /^1\d{10}$/ // 以1開頭的11位數字
if (num && !reg.test(num)) {
return num
}
return num.toString().replace(/(\d{3})\d{4}(\d{3})/, '$1****$2')
}
用mocha寫一下基本的測試用例(關於測試用例的編寫,這裏不做闡述,後續再寫一篇小記記錄一下),校驗一下結果
describe('手機號脫敏', function() {
it('輸入:13580378064 輸出: 135****8064', function() {
assert.equal(decodeMobile(13580378064), '135****8064')
})
it('輸入:23580378064 輸出: 23580378064', function() {
assert.equal(decodeMobile(23580378064), '23580378064')
})
it('輸入:123456789abc 輸出:123456789abc', function() {
assert.equal(decodeMobile('123456789abc'), '123456789abc')
})
it('輸入:"" 輸出:""', function() {
assert.equal(decodeMobile(''), '')
})
})
如下圖所示:
2. 電子郵箱匹配
諸如[email protected]
[email protected]
,這些都是郵箱地址,這次我們來從一串字符中讀取出類似的字符串出來,直接上代碼:
第一版
function getEmail(str) {
const reg = new RegExp(/\w+@\w+\.com/)
return str.match(reg)
}
測試如下:
當輸入: “[email protected], test, [email protected]”,並沒有全局搜索,而是找到第一個結果之後就返回結果數組,這時候要加上 g 全局匹配
第二版
function getEmail(str) {
var reg = new RegExp(/\w+@\w+\.com/g)
return str.match(reg)
}
結果很完美,如圖:
郵箱的規則暫時想到這麼多,貌似還有一些特殊情況還沒考慮到,關於正則的小記先暫且一段落吧。