JavaScript正則表達(dá)式常用技巧

插圖

正則表達(dá)式是用于匹配字符串中字符組合的模式。在 JavaScript 中休弃,正則表達(dá)式也是對(duì)象。這些模式被用于 RegExp 的 exectest 方法, 以及 String 的 matchmatchAll怔球、replacesearchsplit 方法浮还。正則表達(dá)式的掌握程度能粗略地看出程序員的技術(shù)底子竟坛,所以技術(shù)面試、編程競(jìng)賽等 都特別喜歡考察正則表達(dá)式钧舌。本篇就帶你一起夯實(shí)一下 JavaScript 正則表達(dá)式的一些使用技巧:

創(chuàng)建正則表達(dá)式

在 JavaScript 的世界担汤,創(chuàng)建正則表達(dá)式有2個(gè)方法:
(1)使用一個(gè)正則表達(dá)式字面量,其由包含在斜杠之間的模式組成洼冻。比如 :

const reg = /ab+c/

(2)調(diào)用RegExp對(duì)象的構(gòu)造函數(shù)崭歧。比如:

const reg = new RegExp("ab+c")

注意:以上2個(gè)方法雖然都能創(chuàng)建正則表達(dá)式,但是還是有區(qū)別的:
(1)使用第一個(gè)方法撞牢,在腳本加載后正則表達(dá)式字面量就會(huì)被編譯率碾。當(dāng)正則表達(dá)式保持不變時(shí)叔营,使用此方法可獲得更好的性能。
(2)使用第二個(gè)方法所宰,在腳本運(yùn)行過(guò)程中用構(gòu)造函數(shù)創(chuàng)建的正則表達(dá)式會(huì)被編譯绒尊。如果正則表達(dá)式將會(huì)改變,或者它將會(huì)從用戶輸入等來(lái)源中動(dòng)態(tài)地產(chǎn)生仔粥,就需要使用構(gòu)造函數(shù)來(lái)創(chuàng)建正則表達(dá)式婴谱。

當(dāng)然,這樣表述可能不太深刻件炉,下面找一道面試題帶你實(shí)踐一下勘究。

經(jīng)典面試題 "Word Finder"

題目要求:
使用一個(gè)方法來(lái)擴(kuò)展字典,該方法返回與模式匹配的單詞列表斟冕。這個(gè)模式可以包含字母(小寫)和占位符("?")口糕。占位符只代表一個(gè)任意的字母,比如: 

const fruits = new Dictionary(['banana', 'apple', 'papaya', 'cherry']);
fruits.getMatchingWords('lemon');     // must return []
fruits.getMatchingWords('cherr??');   // must return []
fruits.getMatchingWords('?a?a?a');    // must return ['banana', 'papaya']
fruits.getMatchingWords('??????');    // must return ['banana', 'papaya', 'cherry']

補(bǔ)充說(shuō)明:
(1)單詞和模式都是小寫
(2)返回單詞的順序無(wú)關(guān)緊要

上面這道題目是典型的 正則表達(dá)式應(yīng)用題磕蛇,考察的知識(shí)點(diǎn)是2個(gè):
(1)使用 RegExp 對(duì)象 動(dòng)態(tài)創(chuàng)建正則表達(dá)式
(2)使用 /./ 匹配一個(gè)任意字符

因此不難有如下解決方案(ps:這個(gè)是我的解決方案景描,雖然解法比較low,但是邏輯應(yīng)該還算清晰秀撇,容易理解)

// 字典構(gòu)造器
function Dictionary(words) {
  this.words = words;
}

// 原型里拓展解法
Dictionary.prototype.getMatchingWords = function(pattern) {
  let res = []
  const reg = new RegExp("^" + pattern.replace(/\?/g, '.') + "$")  // 創(chuàng)建正則表達(dá)式
  for (let x of this.words) {
    if (reg.test(x)) res.push(x) 
  }
  return res
}

如果你有更好的解法超棺,歡迎評(píng)論留言哈 _

正則表達(dá)式模式

一個(gè)正則表達(dá)式模式是由簡(jiǎn)單的字符所構(gòu)成的,比如 /abc/呵燕;或者是簡(jiǎn)單和特殊字符的組合棠绘,比如 /ab*c//Chapter (\d+)\.\d*/

簡(jiǎn)單模式

簡(jiǎn)單模式是由想要匹配的具體字符組成,且嚴(yán)格匹配字符順序再扭。比如氧苍,/abc/ 這個(gè)模式就能且僅能匹配 "abc" 字符按照順序同時(shí)出現(xiàn)的情況,而 "bac" 或 "cab" 等就無(wú)法匹配泛范。

特殊字符

當(dāng)需要匹配一個(gè)不確定的字符串時(shí)让虐,比如尋找一個(gè)或多個(gè) "b",或者尋找空格罢荡,可以在模式中使用特殊字符赡突。特殊字符還包括如下:

  • 斷言:表示一個(gè)匹配在某些條件下發(fā)生。斷言包括先行斷言区赵、后行斷言和條件表達(dá)式
  • 字符類:區(qū)分不同類型的字符惭缰,例如區(qū)分字母和數(shù)字
  • 組和范圍:表示表達(dá)式字符的分組和范圍
  • 量詞:表示匹配的字符或條件表達(dá)式的數(shù)量
  • Unicode屬性轉(zhuǎn)義:基于 Unicode字符屬性區(qū)分字符,例如大寫和小寫字母笼才、數(shù)字符合和標(biāo)點(diǎn)

Escaping

當(dāng)需要使用任何特殊字符的字面值(例如从媚,搜索字符 *),你必須通過(guò)在它前面放一個(gè)反斜杠來(lái)轉(zhuǎn)義它患整。 例如拜效,要搜索'a'后跟*后跟'b'喷众,你應(yīng)該使用 /a\*b/- 反斜杠“轉(zhuǎn)義”字符 *,使其成為文字而非特殊符號(hào)紧憾。將用戶輸入轉(zhuǎn)義為正則表達(dá)式中的一個(gè)字面字符串到千,可以通過(guò)簡(jiǎn)單的替換來(lái)實(shí)現(xiàn):

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");   //$&表示整個(gè)被匹配的字符串
}

使用正則表達(dá)式

前面講到,正則表達(dá)式可以被用于 RegExp 的 exectest 方法以及 String 的 match赴穗、replace憔四、searchsplit 方法。這些方法在 JavaScript 手冊(cè) 中有詳細(xì)的解釋般眉,下面只簡(jiǎn)單羅列下各自功能了赵,不做展開:

  • exec:在字符串中執(zhí)行查找匹配的 RegExp 方法,它返回一個(gè)數(shù)組(未匹配到則返回 null
  • test:在字符串中測(cè)試是否匹配的 RegExp 方法甸赃,它返回 truefalse
  • match:在字符串中執(zhí)行查找匹配的 String 方法柿汛,它返回一個(gè)數(shù)組,在未匹配到時(shí)會(huì)返回 null
  • matchAll:在字符串中執(zhí)行查找所有匹配的 String 方法埠对,它返回一個(gè)迭代器(iterator
  • search:在字符串中測(cè)試匹配的 String 方法络断,它返回匹配到的位置索引,或者在失敗時(shí)返回 -1
  • replace:在字符串中執(zhí)行查找匹配的 String 方法项玛,并且使用替換字符串替換掉匹配到的子字符串
  • split:使用正則表達(dá)式或者一個(gè)固定字符串分隔一個(gè)字符串的String方法貌笨,并將分隔后的子字符串存儲(chǔ)到數(shù)組中

一個(gè)簡(jiǎn)單的快速記憶方法:
(1)想要知道在一個(gè)字符串中的一個(gè)匹配是否被找到,使用 testsearch 方法
(2)想得到更多的信息(但是比較慢)則可以使用 execmatch 方法

舉個(gè)栗子襟沮,使用exec方法在一個(gè)字符串中查找一個(gè)匹配:

const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");

如果不需要訪問正則表達(dá)式的屬性锥惋,這個(gè)腳本通過(guò)另一個(gè)方法來(lái)創(chuàng)建myArray:

const myArray = /d(b+)d/g.exec("cdbbdbsbz");
// 和 "cdbbdbsbz".match(/d(b+)d/g); 相似。
// 但是 "cdbbdbsbz".match(/d(b+)d/g) 輸出數(shù)組 [ "dbbd" ]开伏,
// 而 /d(b+)d/g.exec('cdbbdbsbz') 輸出數(shù)組 [ "dbbd", "bb", index: 1, input: "cdbbdbsbz" ].

如果想通過(guò)一個(gè)字符串構(gòu)建正則表達(dá)式膀跌,那么這個(gè)腳本還有另一種方法:

const myRe = new RegExp("d(b+)d", "g");
const myArray = myRe.exec("cdbbdbsbz");

使用括號(hào)的子字符串匹配

一個(gè)正則表達(dá)式模式使用括號(hào),將導(dǎo)致相應(yīng)的子匹配被記住硅则。例如淹父,/a(b)c / 可以匹配字符串“abc”株婴,并且記得“b”怎虫。回調(diào)這些括號(hào)中匹配的子串困介,使用數(shù)組元素[1],……[n]大审。

使用括號(hào)匹配的子字符串的數(shù)量是無(wú)限的。返回的數(shù)組中保存所有被發(fā)現(xiàn)的子匹配座哩。下面的例子說(shuō)明了如何使用括號(hào)的子字符串匹配徒扶。

下面的腳本使用 replace() 方法來(lái)轉(zhuǎn)換字符串中的單詞。在匹配到的替換文本中根穷,腳本使用替代的$1, $2 表示第一個(gè)和第二個(gè)括號(hào)的子字符串匹配:

const re = /(\w+)\s(\w+)/;
const str = "John Smith";
const newstr = str.replace(re, "$2, $1");
console.log(newstr);   // 輸出 "Smith, John"

通過(guò)標(biāo)志進(jìn)行高級(jí)搜索

正則表達(dá)式有六個(gè)可選參數(shù) (flags) 允許全局和不分大小寫搜索等姜骡。這些參數(shù)既可以單獨(dú)使用也能以任意順序一起使用, 并且被包含在正則表達(dá)式實(shí)例中:

  • g:全局搜索
  • i:不區(qū)分大小寫搜索
  • m: 多行搜索
  • s:允許.匹配換行符
  • u:使用Unicode碼的模式進(jìn)行匹配
  • y:執(zhí)行“粘性(sticky)”搜索, 匹配從目標(biāo)字符串的當(dāng)前位置開始

例如导坟,re = /\w+\s/g 將創(chuàng)建一個(gè)查找一個(gè)或多個(gè)字符后有一個(gè)空格的正則表達(dá)式,或者組合起來(lái)像此要求的字符串:

const re = /\w+\s/g;
const str = "fee fi fo fum";
const myArray = str.match(re);
console.log(myArray);

// ["fee ", "fi ", "fo "]

使用 .exec() 方法時(shí)圈澈,與 g 標(biāo)志關(guān)聯(lián)的行為是不同的惫周。 (“class”和“argument”的作用相反:在.match()的情況下,字符串類(或數(shù)據(jù)類型)擁有該方法康栈,而正則表達(dá)式只是一個(gè)參數(shù)递递,而在.exec()的情況下,它是擁有該方法的正則表達(dá)式啥么,其中字符串是參數(shù)登舞。對(duì)比str.match(re)re.exec(str) ), g標(biāo)志與.exec()方法一起使用獲得迭代進(jìn)展:

const xArray; while(xArray = re.exec(str)) console.log(xArray);
// produces: 
// ["fee ", index: 0, input: "fee fi fo fum"]
// ["fi ", index: 4, input: "fee fi fo fum"]
// ["fo ", index: 7, input: "fee fi fo fum"]

除此之外,m標(biāo)志用于指定多行輸入字符串應(yīng)該被視為多個(gè)行悬荣。如果使用m標(biāo)志菠秒,^$匹配的開始或結(jié)束輸入字符串中的每一行,而不是整個(gè)字符串的開始或結(jié)束隅熙。


@參考:正則表達(dá)式

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布稽煤!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市囚戚,隨后出現(xiàn)的幾起案子酵熙,更是在濱河造成了極大的恐慌,老刑警劉巖驰坊,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匾二,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拳芙,警方通過(guò)查閱死者的電腦和手機(jī)察藐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)舟扎,“玉大人分飞,你說(shuō)我怎么就攤上這事《孟蓿” “怎么了譬猫?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)羡疗。 經(jīng)常有香客問我染服,道長(zhǎng)种樱,這世上最難降的妖魔是什么壳坪? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮咳秉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秉颗。我一直安慰自己痢毒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布蚕甥。 她就那樣靜靜地躺著闸准,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梢灭。 梳的紋絲不亂的頭發(fā)上夷家,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音敏释,去河邊找鬼库快。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钥顽,可吹牛的內(nèi)容都是我干的义屏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜂大,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼闽铐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起奶浦,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兄墅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后澳叉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隙咸,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年成洗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了五督。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓶殃,死狀恐怖充包,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遥椿,我是刑警寧澤基矮,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站修壕,受9級(jí)特大地震影響愈捅,放射性物質(zhì)發(fā)生泄漏遏考。R本人自食惡果不足惜慈鸠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧青团,春花似錦譬巫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至娃肿,卻和暖如春咕缎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背料扰。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工凭豪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晒杈。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓嫂伞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拯钻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帖努,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354