JavaScript正則進階之路——活學妙用奇淫正則表達式

原文收錄在個人 GitHub博客 (https://github.com/jawil/blog) ,喜歡的能夠關注最新動態,你們一塊兒多交流學習,共同進步,以學習者的身份寫博客,記錄點滴。html

有些童鞋確定有所疑惑,花了大量時間學習正則表達式,卻發現沒有用武之地,正則不就是驗證個郵箱嘛,其餘地方基本用不上,其實,大部分人都是這種感受,因此有些人乾脆不學,以爲又難又沒多大用處。卻不知,想要成爲編程大牛,正則表達式必須玩轉,GitHub上優秀的開源庫和框架裏面處處都是強大的正則匹配,當年jQuery做者也被稱爲正則小王子。這裏分享一些工做中用到的和本身收集的一些正則表達式的妙用,處處閃耀着開發者智慧的火花。前端

實現一個需求的方法不少種,哪一種更好,仁者見仁智者見智,這裏只提供一種對比的思惟來激發你們學習正則的興趣和養成活用正則的思惟。git

做爲前端開發人員,總會有點本身的奇技淫巧,畢竟前端開發不一樣於後端,代碼所有暴漏給用戶不說,代碼冗餘了少則影響帶寬,多則效率下降。正則表達式(Regular Expression),這是一塊硬骨頭,很難啃,可是啃着又很香。因此今天我也來爆一些正則表達式的奇技淫巧。github

正則大法好,正則大法好,正則大法好,重要的事情說三遍。正則表達式

一、獲取連接 https://www.baidu.com?name=jawil&age=23 name的value值

非正則實現:算法

function getParamName(attr) {

  let search = window.location.search // "?name=jawil&age=23"

  let param_str = search.split('?')[1] // "name=jawil&age=23"

  let param_arr = param_str.split('&') // ["name=jawil", "age=23"]

  let filter_arr = param_arr.filter(ele => { // ["name=jawil"]
    return ele.split('=')[0] === attr
  })

  return decodeURIComponent(filter_arr[0].split('=')[1])
}

console.log(getParamName('name')) // "jawil"

用正則實現:express

function getParamName(attr) {

  let match = RegExp(`[?&]${attr}=([^&]*)`) //分組運算符是爲了把結果存到exec函數返回的結果裏
    .exec(window.location.search)
  //["?name=jawil", "jawil", index: 0, input: "?name=jawil&age=23"]
  return match && decodeURIComponent(match[1].replace(/\+/g, ' ')) // url中+號表示空格,要替換掉
}
  
console.log(getParamName('name'))  // "jawil"

看不太懂先學習一下這篇文章:[[ JS 進階 ] test, exec, match, replace](https://segmentfault.com/a/11...編程

二、 數字格式化問題,1234567890 --> 1,234,567,890

非正則實現:segmentfault

let test = '1234567890'

function formatCash(str) {
  let arr = []

  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))

    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))

  }

  return arr.join(',')
}

console.log(formatCash(test)) // 1,234,567,890

用正則實現:後端

let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

console.log(format) // 1,234,567,890

下面簡單分析下正則/\B(?=(\d{3})+(?!\d))/g

  1. /\B(?=(\d{3})+(?!\d))/g:正則匹配邊界\B,邊界後面必須跟着(\d{3})+(?!\d);

  2. (\d{3})+:必須是1個或多個的3個連續數字;

  3. (?!\d):第2步中的3個數字不容許後面跟着數字;

  4. (\d{3})+(?!\d):因此匹配的邊界後面必須跟着3*n(n>=1)的數字。

最終把匹配到的全部邊界換成,便可達成目標。

三、去掉字符串左右兩邊的空格," jaw il " --> 「jaw il」

非正則實現:

function trim(str) {
    let start, end
    for (let i = 0; i < str.length; i++) {
        if (str[i] !== ' ') {
            start = i
            break
        }
    }
    for (let i = str.length - 1; i > 0; i--) {
        if (str[i] !== ' ') {
            end = i
            break
        }
    }

    return str.substring(start, end - 1)
}


let str = "  jaw il "
console.log(trim(str)) // "jaw il"

用正則實現:

function trim(str) {
    return str.replace(/(^\s*)|(\s*$)/g, "")
}

let str = "  jaw il "
console.log(trim(str)) // "jaw il"

四、判斷一個數是不是質數 3 --> true

質數又稱素數。指在一個大於1的天然數中,除了1和此整數自身外,無法被其餘天然數整除的數。

非正則實現:

function isPrime(num){
    // 不是數字或者數字小於2
    if(typeof num !== "number" || !Number.isInteger(num)){      
    // Number.isInterget 判斷是否爲整數
        return false
    }

    //2是質數
    if(num == 2){
        return true
    }else if(num % 2 == 0){  //排除偶數
        return false
    }
    //依次判斷是否能被奇數整除,最大循環爲數值的開方
    let squareRoot = Math.sqrt(num)
    //由於2已經驗證過,因此從3開始;且已經排除偶數,因此每次加2
    for(let i = 3; i <= squareRoot; i += 2) {
      if (num % i === 0) {
         return false
      }
    }
    return true
}

console.log(isPrime(19)) // true

用正則實現:

function isPrime(num) {
return !/^1?$|^(11+?)\1+$/.test(Array(num+1).join('1'))
}

console.log(isPrime(19)) // true

要使用這個正規則表達式,你須要把天然數轉成多個1的字符串,如:2 要寫成 「11」, 3 要寫成 「111」, 17 要寫成「11111111111111111」,這種工做使用一些腳本語言能夠輕鬆的完成,JS實現也很簡單,我用Array(num+1).join('1')這種方式實現了一下。

一開始我對這個表達式持懷疑態度,但仔細研究了一下這個表達式,發現是很是合理的,下面,讓我帶你來細細剖析一下是這個表達式的工做原理。

首先,咱們看到這個表達式中有「|」,也就是說這個表達式能夠分紅兩個部分:/^1?$//^(11+?)\1+$/

  • 第一部分:/^1?$/, 這個部分相信不用我多說了,其表示匹配「空串」以及字串中只有一個「1」的字符串。

  • 第二部分:/^(11+?)1+$/ ,這個部分是整個表達式的關鍵部分。其能夠分紅兩個部分,(11+?) 和 1+$ ,前半部很簡單了,匹配以「11」開頭的並重復0或n個1的字符串,後面的部分意思是把前半部分做爲一個字串去匹配還剩下的字符串1次或屢次(這句話的意思是——剩餘的字串的1的個數要是前面字串1個數的整數倍)。

可見這個正規則表達式是取非素數,要獲得素數還得要對整個表達式求反。經過上面的分析,咱們知道,第二部分是最重要的,對於第二部分,舉幾個例子,

示例一:判斷天然數8。咱們能夠知道,8轉成咱們的格式就是「11111111」,對於 (11+?) ,其匹配了「11」,因而還剩下「111111」,而 1+$ 正好匹配了剩下的「111111」,由於,「11」這個模式在「111111」出現了三次,符合模式匹配,返回true。因此,匹配成功,因而這個數不是質數。

示例二:判斷天然數11。轉成咱們須要的格式是「11111111111」(11個1),對於 (11+?) ,其匹配了「11」(前兩個1),還剩下「111111111」(九個1),而 1+$ 沒法爲「11」匹配那「九個1」,由於「11」這個模式並無在「九個1」這個串中正好出現N次。因而,咱們的正則表達式引擎會嘗試下一種方法,先匹配「111」(前三個1),而後把「111」做爲模式去匹配剩下的「11111111」(八個1),很明顯,那「八個1」並無匹配「三個1」屢次。因此,引擎會繼續向下嘗試……直至嘗試全部可能都沒法匹配成功。因此11是素數。

經過示例二,咱們能夠獲得這樣的等價數算算法,正則表達式會匹配這若干個1中有沒有出現「二個1」的整數倍,「三個1」的整數倍,「四個1」的整數倍……,而,這正好是咱們須要的算素數的算法。如今你們明白了吧。

五、字符串數組去重 ["a","b","c","a","b","c"] --> ["a","b","c"]

這裏只考慮最簡單字符串的數組去重,暫不考慮,對象,函數,NaN等狀況,這種用正則實現起來就吃力不討好了。

非正則實現:

①ES6實現

let str_arr=["a","b","c","a","b","c"]

function unique(arr){
  return [...new Set(arr)]
}

console.log(unique(str_arr)) // ["a","b","c"]

②ES5實現

var str_arr = ["a", "b", "c", "a", "b", "c"]

function unique(arr) {
    return arr.filter(function(ele, index, array) {
        return array.indexOf(ele) === index
    })
}

console.log(unique(str_arr)) // ["a","b","c"]

③ES3實現

var str_arr = ["a", "b", "c", "a", "b", "c"]

function unique(arr) {
    var obj = {},
        array = []

    for (var i = 0, len = arr.length; i < len; i++) {
        var key = arr[i] + typeof arr[i]
        if (!obj[key]) {
            obj[key] = true
            array.push(arr[i])
        }
    }
    return array
}

console.log(unique(str_arr)) // ["a","b","c"]

額,ES4呢。。。對不起,因爲歷史緣由,ES4改動太大,因此被廢棄了。
能夠看到從ES3到ES6,代碼愈來愈簡潔,JavaScript也愈來愈強大。

用正則實現:

var str_arr = ["a", "b", "c", "a", "b", "c"]

function unique(arr) {
    return arr.sort().join(",,").
    replace(/(,|^)([^,]+)(,,\2)+(,|$)/g, "$1$2$4").
    replace(/,,+/g, ",").
    replace(/,$/, "").
    split(",")
}

console.log(unique(str_arr)) // ["a","b","c"]

這裏我只是拋磚引玉的利用幾個例子對比來展示正則表達式的強大,其實正則表達式的應用遠遠不止這些,這裏列出的只是冰山一角,更多的奇淫技巧須要大家來創造,知識點API是有限的,技巧和創造倒是無限的,歡迎你們開動腦門,創造或分享本身的奇淫技巧。

學習正則

若是尚未系統學習正則表達式,這裏提供一些網上經典的教程供你們學習。

正則表達式(Regular Expression),這是一塊硬骨頭,很難啃,可是啃着又很香。

正則表達式使用單個字符串來描述、匹配一系列匹配某個句法規則的字符串。不少地方咱們都須要使用正則,因此今天就將一些優秀的教程,工具總結起來。

基本內容

https://en.wikipedia.org/wiki/Regular_expression 瞭解同樣東西,固然先從WIKI開始最好了。

// Regular Expression examples
I had a \S+ day today
[A-Za-z0-9\-_]{3,16}
\d\d\d\d-\d\d-\d\d
v(\d+)(\.\d+)*
TotalMessages="(.*?)"
<[^<>]>

教程

http://deerchao.net/tutorials/regex/regex.htm 30分鐘入門教程,網上流傳甚廣
https://qntm.org/files/re/re.html 55分鐘教程【英文】,
http://regex.learncodethehardway.org/book/ 一本簡單的書,每一節就是一塊內容
https://swtch.com/~rsc/regexp/regexp1.html 正則匹配原理解析
http://stackoverflow.com/tags/regex/info stackoverflow 正則標籤,標籤下有值得點擊的連接,一些典型的問題
http://regexr.com/ 正則學習測試於一身
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions MDN出品,JavaScript方面內容

驗證與測試

https://regex101.com/ in JavaScript, Python, PCRE 16-bit, generates explanation of pattern
https://www.debuggex.com/ 正則驗證測試,清晰明瞭
https://mengzhuo.org/regex/ 中文版正則驗證測試
http://refiddle.com/ 測試工具
http://myregexp.com/ 也是測試工具,均可以試一試

闖關模式實踐

http://regex.alf.nu 闖關模式練習正則表達式,完成一個個正則匹配的測驗
http://regexone.com/ 經過實際練習掌握正則表達式
https://regexcrossword.com/ 正則挑戰,有不一樣難度,很豐富
http://callumacrae.github.io/regex-tuesday/ 正則挑戰,完成正則匹配要求

其它

https://msdn.microsoft.com/zh-cn/library/az24scfc.aspx MSDN 微軟出品
http://www.jb51.net/tools/regex.htm 經常使用正則表達式,如匹配網址、日期啊這種,這個谷歌一搜不少的
https://www.cheatography.com/davechild/cheat-sheets/regular-expressions/ 速查表地址,以下圖

相關文章
相關標籤/搜索