別再說概念了!直接告訴我這些常用正則表達(dá)式是怎么寫出來的行不行?
點(diǎn)擊上方 前端Q,關(guān)注公眾號
回復(fù)加群,加入前端Q技術(shù)交流群
作為一名程序員,不會寫正則表達(dá)式總感覺少了點(diǎn)什么,不要求你能把正則玩出花來,但最起碼要對常用的正則表達(dá)式手到擒來,剛畢業(yè)的我對于正則也是一頭霧水,不過學(xué)會它也就一篇教程的事情[1]
本文我不會再浪費(fèi)帶寬把正則的規(guī)則再次重復(fù)一遍,而是從實際入手,直接告訴你為什么這么寫
16進(jìn)制顏色
按照規(guī)則來
以 #開頭后面緊跟著6個字符或者3個字符作為結(jié)尾,這些字符可以是 a-f的小寫字母、A-F的大寫字母、數(shù)字
第一句,可以寫成 /^#/;第二句,[a-fA-F0-9] 表示任意的 a-f、A-F、0-9,6或 3的個數(shù)可以用 {6}、{3}進(jìn)行表示,那么3個字符就是 [a-fA-F0-9]{3},6個字符就是 [a-fA-F0-9]{6},這兩個都有可能,用一個或(|)符號來連接:([a-fA-F0-9]{6}|[a-fA-F0-9]{3}),最后結(jié)尾可以用個 $
所有合到一起就是 /^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
鏈接
目標(biāo)是匹配出協(xié)議、域名、端口號port、path、search
協(xié)議
合法的協(xié)議有 http、https,還有一個是自適應(yīng)協(xié)議,即不明確加協(xié)議,跟當(dāng)前頁面的協(xié)議保持一致,所以以下都是合法的:http://toutiao.com、https://toutiao.com、//toutiao.com。這三個協(xié)議組成的鏈接共同點(diǎn)是肯定有 // 字符串,在 //的前面可能是 https: 也可能是 http: 也可以沒有任何字符串 先按照 https:// 這種寫規(guī)則:^https:\/\/,其中的 s 字符可能有也可能沒有,所以使用 ? 修飾:^https?:\/\/,又因為 https?:可能沒有,所以這個字符串也用 ?修飾:^(https?:)?\/\/
域名
域名的前面可能是 //,從 //往后面匹配,只要沒有代表 :的 port、代表 search 的 ?、代表 path的 /,那么就都屬于域名:[^?:/]+
端口號 port
端口號肯定以 : 開頭,后面跟著的只要是數(shù)字就都屬于 port::\d+,由于不一定有端口號,所以用 ? 修飾:(:\d+)?
path
肯定以 / 開頭,只要不遇到代表 search 的 ?,那么就都屬于 path:\/[^?]*,由于可能沒有 path,所以用 ? 修飾:(\/[^?]*)? 5. search 肯定以 ? 開頭,后面所有的字符都屬于 search(不考慮 hash 路由):\?(.*),由于可能沒有 search,所以用 ? 修飾:(\?.*)?
最后把上面所有規(guī)則合起來就是提取鏈接的完整正則了,考慮到需要精確提取所需要的部分,所以會對所需要提取的部分加上小括號,結(jié)果為:/^((https?):)?\/\/([^?:/]+)(:(\d+))?(\/[^?]*)?(\?(.*))?/
郵箱
以前在知乎上看到過一段郵箱正則,號稱是最符合標(biāo)準(zhǔn)的正則表達(dá)式,那條正則的體積好像有幾十KB吧,總之很長,現(xiàn)在找不到了,這里只關(guān)注常用的郵箱格式,規(guī)則:名稱允許漢字、字母、數(shù)字,下劃線,中劃線,域名可以有數(shù)字、字母、下劃線、中劃線組成
漢字的范圍是 [\u4e00-\u9fa5],字母的范圍是 [a-zA-Z],數(shù)字的范圍是 [0-9],合起來組成郵箱的名稱 ^[A-Za-z0-9-_\u4e00-\u9fa5]+
域名是 [a-zA-Z0-9_-]+,域名后綴可以是多級域名 (\.[a-zA-Z0-9_-]+)+
上面組合起來就是 ^[A-Za-z0-9-_\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
手機(jī)號
手機(jī)號的號段可能是會增加的,所以在實際場景中不建議限制得太死了
本正則按照以下規(guī)則編寫:
11位數(shù)字,以數(shù)字 1開頭,即^1接下來的數(shù)字如果是 3,那么3后面可以跟一個任意數(shù)字,即3\d;如果是4,那么4后面可以跟一個5-9之間的數(shù)字,即4[5-9];如果是5,那么5后面可以跟一個0-3或5-9之間的數(shù)字,即5[0-35-9];如果是6,那么6后面可以跟2、5、6、7其中一個數(shù)字,即6[2567];如果是7,那么7后面可以跟一個0-8之間的數(shù)字,即7[0-8];如果是8,那么8后面可以跟一個任意數(shù)字,即8\d;如果是9,那么9后面可以跟一個0-3或5-9之間的數(shù)字,即9[0-35-9]最后 8位可以是任意數(shù)字
上述三步合起來就是 /^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
數(shù)字/貨幣金額
支持負(fù)數(shù)
負(fù)號用 -負(fù)號,且必須在第一位,即 ^-,再加個 ? 用于表示這個負(fù)號可以有也可以沒有,即 ^-? 2. 支持千分位分隔(沒有也沒關(guān)系) 如果有千分位,則千分位的后面必然跟著三位數(shù)字(否則這個千分號就不應(yīng)該加了),千分位前面最少一位、最多三位數(shù)字,那么可以寫成 \d{1,3},\d{3},再精簡下,千分位前面的數(shù)字其實可以不用限制,因為只要超過三位肯定就有千分位,就會被 \d{3}捕獲,所以 \d{1,3} 換成 \d+就行了,因為符合千分位的可以有多個也可能沒有,所以寫成 \d+(,\d{3})* 3. 如果有小數(shù),則小數(shù)點(diǎn)后最多兩位 小數(shù)點(diǎn)就是 \.,后面跟著最多兩位數(shù)字 \d{1,2},可能有小數(shù)也可能沒有,所以整體需要再加個 ? 符號,即 (\.\d{1,2})?
最終規(guī)則 /^-?\d+(,\d{3})*(\.\d{1,2})?$/
身份證號
這里只看 2代身份證,18位數(shù)字 最后一位是校驗位,可能為數(shù)字或字符X
第一位數(shù)字在
[1-9]閉區(qū)間內(nèi),后面緊跟著5位任意數(shù)字,寫成^[1-9]\d{5}再緊跟著的四位數(shù)字代表年份(
YYYY),因為目前有身份證的人最早是19世紀(jì)最晚21世紀(jì),所以這四個數(shù)字中的前兩位只看是18、19、20,即(18|19|20),后兩位則可以是任意數(shù)字,即\d{2}再緊跟著兩位數(shù)字是月(
MM),月份只可能是 [1-12]閉區(qū)間,所以可以寫成(01|02|03|04|05|06|07|08|09|10|11|12),前九位的開頭都是0,第二位是 [1-9] 內(nèi)的數(shù)字,所以簡化成(0[1-9]|10|11|12)再緊跟著兩位數(shù)字是日(
DD),范圍是 [01-31],可以將這31個數(shù)字羅列出來,當(dāng)然也可以精簡下,看成是[00-09]、[10-29]、[30-31]的組合,即(0[1-9]|[1-2]\d|30|31)再緊跟著三位數(shù)字是順序碼,即
\d{3}最后一位是校驗碼,可以是數(shù)字也可以是
X,即[\dX]
最終規(guī)則 /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|10|11|12)(0[1-9]|[1-2]\d|30|31)\d{3}[\dX]$/
密碼校驗
最少6位,包括至少1個大寫字母,1個小寫字母,1個數(shù)字,1個特殊字符
對于 至少1個大寫字母 這條規(guī)則,這個大寫字母的位置是不固定的,只要有就行,如果只有這一條規(guī)則的話,正則可以寫成 ^\S*[A-Z]+\S*$,\S 匹配任意非空白字符,這個規(guī)則即代表大寫字母的前面、后面可以跟著任意個(包括0個)非空白字符
但除此之外還需要滿足最少1個小寫字母,1個數(shù)字,1個特殊字符,最少6位,你可以將這幾條規(guī)則都單獨(dú)寫出正則,然后目標(biāo)字符串跟這5條正則一一匹配,只要全部能匹配上就是對的,寫成 js 代碼就是:
function match(s: string) {
return /^\S*[A-Z]+\S*$/.test(s)
&& /^\S*[a-z]+\S*$/.test(s)
&& /^\S*[0-9]+\S*$/.test(s)
&& /^\S*[!@#$%^&*?]+\S*$/.test(s)
&& /^\S*\S{6,}\S*$/.test(s)
}
復(fù)制代碼
如果就想在一條正則里實現(xiàn)這些校驗?zāi)?,也是可以的,需要借?零寬度正預(yù)測先行斷言 ((?=exp)),代表 匹配exp前面的位置
有了這個東西,就可以把上面5條規(guī)則寫到一起去了:/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*?])\S*$/
這條正則前面后面的 \S* 還是之前的意思不變,中間是提取了5條規(guī)則的個性部分,然后通過 ?= 放在一起了,把規(guī)則里所有的 ?=去掉行不行?不行,因為如果去掉的話,首先就有順序上的沖突了,例如上面的規(guī)則,如果把所有的 ?=去掉,就代表著 數(shù)字必須在大寫字母前面,大寫字母必須在小寫字母前面,特殊字母必須在小寫字母前面(除此之外整個正則也是有問題的)
你可以認(rèn)為 ?= 在匹配的時候會忽略掉其他的 ?=,只關(guān)心自己的前面能不能匹配成功,有多個 ?=,則這多個 ?= 都是只關(guān)心自己,忽略其他,但整條正則最后的結(jié)果是所有 ?= 匹配結(jié)果的并集,計算邏輯和上面的 js 是差不多的
提取 HTML 標(biāo)簽數(shù)據(jù)
要提取的標(biāo)簽字符串類似于 <div class="header-box" name="header">
只是正則話無法完成,需要借助 js
首先,把標(biāo)簽的屬性提取出來
這段標(biāo)簽包含標(biāo)簽開始符號、tag、屬性字符串、標(biāo)簽結(jié)束符號
開始符號是 <,標(biāo)簽名緊跟著開始符號,且只要沒遇到空白符就都是標(biāo)簽名,所以連起來就是 <\w+\s*
在開始符號+標(biāo)簽名,和 結(jié)束符號的中間,都是屬性,結(jié)束符號是 >,所以只要沒遇到結(jié)束符號 >,就認(rèn)為是屬性字符串,用到了反向選擇 [^>]*\s*>,合起來就是 /<\w+\s*[^>]*\s*>/,為了能捕獲屬性字符串,加個小括號,即 /<\w+\s*([^>]*)\s*>/
const str = `<div class="header-box" name="header">`
const mt = str.match(/<\w+\s*([^>]*)\s*>/)
// properties 即 屬性字符串,即 class="header-box" name="header"
const properties = mt[1]
復(fù)制代碼
取到了 class="header-box" name="header" 之后,再對其進(jìn)行處理,觀察規(guī)律,每個屬性的鍵值對之間肯定存在空白符,不過卻不能通過空白符來直接分割,因為屬性值是可以存在空白符的,例如 class="a b"
由于可能是自閉合標(biāo)簽,自閉合標(biāo)簽的最后有沒有 / 都是合法的,例如 <hr> 和 hr />都是合法的,所以需要兼容下:/<\w+\s*([^>]*)\s*\/?>/
但屬性名是可以確定的,它可能是 = 左邊不包括空白符的內(nèi)容,再次用到反向選擇,從左往右匹配,反向選擇既不是=也不是不是空白符的內(nèi)容,即 [^\s=]+
雖然不確定屬性值是否包含空白符,但有個是確定的,即屬性值必然被引號包圍,所以直接取 = 右側(cè)所有引號的內(nèi)容即可,=".*?"
不過還有個問題,引號不僅可以是單引號還可以是雙引號,即 =".*?" 和 ='.*?' 都行,如果第一個引號是雙引號開頭那么對應(yīng)的第二個引號也必然是雙引號,反義單引號亦然,這里需要用到捕獲的規(guī)則了 =(["']).*?\1,\1的意思是這塊匹配的內(nèi)容跟第一捕獲組一樣,第一捕獲組也就是 ["'],如果第一捕獲組匹配的是雙引號,那么 \1 就代表雙引號,否則就代表單引號
至此整個正則為 [^\s=]+=(["']).*?\1
不過還有個問題,屬性是可以沒有屬性值的,例如 <input type="checkbox" checked />,這里 checked 就是可以不寫屬性值的,所以再兼容下:/[^\s=]+(=(["']).*?\2)?/,又因為希望捕捉屬性和屬性值,所以給屬性和屬性值加個小括號:/([^\s=]+)(=(["'])(.*?)\3)?/
上面的代表就可以繼續(xù)寫了
const str = `<div class="header-box" name="header">`
const mt = str.match(/<\w+\s*([^>]*)\s*>/)
// properties 即 屬性字符串,即 class="header-box" name="header"
const properties = mt[1]
const mt1 = properties.match(/([^\s=]+)(=(["'])(.*?)\3)?/g)
const obj = {}
if (mt1) {
mt1.forEach(p => {
const kv = p.trim().split('=')
obj[kv[0].trim()] = kv[1].trim().slice(1, -1)
})
}
// obj => { class: 'header-box', name: 'header' }
復(fù)制代碼
小結(jié)
學(xué)會正則的最接途徑就是勤加練習(xí),平時遇到可以用正則解決的問題,就嘗試著正則解決,或許你一開始寫不出來,但可以去網(wǎng)上看看別人是怎么寫的,再自己獨(dú)立寫一遍,寫得多了自然就會了,沒什么訣竅,無非就是對正則規(guī)則的熟練掌握罷了
最后
字節(jié)跳動-直播變現(xiàn)與千川部門招聘啦!
不低于 10 個 hc(騙人是小狗),北京、上海、杭州都可以,而且是 急招,就差一個程序員了的那種
經(jīng)驗不限,接受實習(xí)
之前投過字節(jié)其他部門沒過的也沒關(guān)系(面試沒過不一定代表能力不行,也可能是眼緣不夠),可以繼續(xù)面我這個部門(萬一就看對眼了呢,畢竟我們部門真的缺人),有興趣的可將簡歷發(fā)我郵箱 kother\@foxmail.com[2] ,所有發(fā)我簡歷的人,保證全程跟進(jìn)并反饋面試進(jìn)度,有問必答(在不違反公司規(guī)章制度的前提下),杜絕簡歷一投石沉大海的糟糕體驗
救救孩子,快來把我的需求分點(diǎn)走吧!
團(tuán)隊介紹 直播變現(xiàn)與千川,負(fù)責(zé)優(yōu)化字節(jié)跳動中國區(qū)流量的直播廣告以及短視頻電商廣告,負(fù)責(zé)巨量千川的平臺建設(shè)、算法優(yōu)化、廣告產(chǎn)品和運(yùn)營策略的落地。首要目標(biāo)是:借由字節(jié)強(qiáng)大的算法工程能力,發(fā)揮直播體裁和電商閉環(huán)的天然優(yōu)勢,進(jìn)一步提升中國區(qū)商業(yè)化收入。生活服務(wù)業(yè)務(wù)依托于抖音,抖音極速版等平臺,致力于促進(jìn)用戶與本地服務(wù)的連接;過去一年,生活服務(wù)業(yè)務(wù)開創(chuàng)了全新的視頻種草和交易體驗,讓更多用戶通過抖音發(fā)現(xiàn)線下的好去處,也幫助眾多本地商家拓展了新的經(jīng)營陣地。我們期待你的加入,希望下一個參與服務(wù)百萬商家,影響億級消費(fèi)者,引領(lǐng)營銷革命的就是你!
團(tuán)隊優(yōu)勢
技術(shù):以業(yè)務(wù)為最終導(dǎo)向,即使作為研發(fā)角色,也能夠接觸一線客戶,通過技術(shù)方案解決客戶問題,技術(shù)方案涉及廣告投放系統(tǒng)中召回、粗排、精排、出價、排序等多個環(huán)節(jié),能夠深入全面地理解廣告變現(xiàn)系統(tǒng)的各個環(huán)節(jié)的內(nèi)部原理。 成長:字節(jié)電商GMV仍然在高速提升,在滿足購買需求時,短視頻和直播體裁具備顛覆性的優(yōu)勢,業(yè)務(wù)成長空間非常大。 機(jī)遇:字節(jié)電商的購買體驗更加多元,包含:商品、視頻、直播流、達(dá)人、粉絲關(guān)系、直播互動等等,相比傳統(tǒng)貨架式電商,scope更大,個人的發(fā)展機(jī)會更多。
關(guān)于本文
來自:清夜
https://juejin.cn/post/7073360739410378760

往期推薦



最后
歡迎加我微信,拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...
歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個專業(yè)的技術(shù)人...


