JavaScript 與正則表達(dá)式 -- 括號(hào)

在正則表達(dá)式中讨盒,括號(hào)涉及的問題比較多苏潜,所以這里單獨(dú)拿出來講。

分組

如果量詞所限定的元素不是一個(gè)字符或者字符組拔疚,而是一系列字符或者子表達(dá)式肥隆,就需要使用括號(hào)將他們括起來,表示為“一組”稚失,構(gòu)成單個(gè)元素栋艳。

var regex = /(ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) );  
 //  ["abab", "ab", "ababab"]

上面的例子中,量詞 + 的前面的元素是 (ab) 句各, 所以 + 所限定的是括號(hào)內(nèi) ab 這個(gè)整體吸占。

劃定多選結(jié)構(gòu)的范圍

多選結(jié)構(gòu), 也叫 分支結(jié)構(gòu)凿宾。一般的用法: (p1|p2|p3)矾屯,其中,| 表示 “或”初厚,p1件蚕、p2p3 是三個(gè)子表達(dá)式,這些子表達(dá)式也叫多選分支产禾, 括號(hào)用來劃定分支結(jié)構(gòu)的范圍排作。
注意:多選結(jié)構(gòu)中括號(hào)不是必須的。如果沒有括號(hào)亚情,管道符 | 會(huì)把整個(gè)表達(dá)式當(dāng)做一個(gè)多選結(jié)構(gòu)妄痪。比如,要匹配 grey或gray:

var regexRight = /gr(e|a)y/;  // 匹配 grey 或 gray
var regexWrong = /gre|ay/;  // 匹配 gre 或 ay

// 正確的
console.log(regexRight.test('grey'));  // true
console.log(regexRight.test('gray'));  // true
console.log(regexRight.test('gre'));  // false

// 錯(cuò)誤的
console.log(regexWrong.test('grey'));  // true
console.log(regexWrong.test('gre'));   // true

所以楞件,雖然多選結(jié)構(gòu)中括號(hào)不是必須的拌夏,但是,通常會(huì)搭配括號(hào)來使用履因。

多選結(jié)構(gòu)與字符組

上面多選結(jié)構(gòu)中 gr(e|a)y的例子并太好障簿,因?yàn)榭梢允褂酶玫姆绞酱妫潜闶?gr[ae]y栅迄,那么二者什么區(qū)別呢站故?
二者差別還是很大的:

  • 多選結(jié)構(gòu)中每個(gè)分支都必須明確列出。而字符組可以使用 - 表示范圍
  • 大多數(shù)情況下, [abc] 要比 (a|b|c) 更高效
  • 字符組的每個(gè) “分支” 都必須是單個(gè)的字符西篓,而多選結(jié)構(gòu)的“分支”可以是子表達(dá)式
  • 多選結(jié)構(gòu)的分支順序會(huì)影響到最后的配置結(jié)果
  • 沒有 排除型多選結(jié)構(gòu)

引用分組

使用括號(hào)之后愈腾,正則表示會(huì)保存每個(gè)分組真正匹配的文本,等匹配成功后岂津,可以引用這些文本虱黄。
因?yàn)檫@種情況下“捕獲”了文本,所以這種分組叫 捕獲分組吮成,這種括號(hào)叫 捕獲型括號(hào)橱乱。

通過編號(hào)引用

編號(hào)規(guī)則:
如,使用(\d{4})-(\d{2})-(\d{2})匹配日期 2018-12-30:

字符串 2018 12 30
表達(dá)式 (\d{4}) (\d{2}) (\d{2})
分組編號(hào) 1 2 3

注意:
如果把表達(dá)式寫成:(\d){4}-(\d){2}-(\d){2}粱甫,則含義完全不同泳叠,(\d){4} 表示 \d 作為單獨(dú)的元素出現(xiàn)4次,且編號(hào)都為1茶宵。

嵌套規(guī)則:根據(jù)開括號(hào)的出現(xiàn)順序來計(jì)數(shù)危纫。(圖參考《正則指引》P45,我畫的有點(diǎn)丑)

括號(hào)嵌套編號(hào)規(guī)則:開括號(hào)的出現(xiàn)順序

在 JavaScript 中使用

提取數(shù)據(jù)

String.prototype.match() 方法返回一個(gè)數(shù)組乌庶,數(shù)組的第一項(xiàng)是進(jìn)行匹配的完整字符串种蝶,之后的項(xiàng)是捕獲分組的匹配結(jié)果。

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var text = '2018-12-30';
console.log(text.match(regex));
// ["2018-12-30", "2018", "12", "30", index: 0, input: "2018-12-30"]

關(guān)于 match 方法瞒大,有一個(gè)地方需要注意蛤吓,返回結(jié)果與正則表達(dá)式是否包含 g 標(biāo)志有關(guān)。在沒有 g 標(biāo)志的時(shí)候糠赦,返回值和 regex.exec() 方法相同:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var text = '2018-12-30';
console.log(regex.exec(text));
// ["2018-12-30", "2018", "12", "30", index: 0, input: "2018-12-30"]

同時(shí)会傲,也可以使用構(gòu)造函數(shù)的全局屬性 $1$9 來獲取引用:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var text = '2018-12-30';
regex.exec(text);

console.log(RegExp.$1);  // 2018
console.log(RegExp.$2);  // 12
console.log(RegExp.$3);  // 30

替換

比如,想把 yyyy-mm-dd 格式拙泽,替換成 mm/dd/yyyy 怎么做?
可以使用下面的三種方法:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var text = '2018-12-30';

// 1
var result1 = text.replace(regex, '$2/$3/$1');

// 2
var result2 = text.replace(regex, () => `${RegExp.$2}/${RegExp.$3}/${RegExp.$1}`);

// 3
var result3 = text.replace(regex, (str, y, m, d) => `${m}/$zzdzsue/${y}`);

console.log(result1);    // 12/30/2018
console.log(result2);    // 12/30/2018
console.log(result3);    // 12/30/2018

String.prototype.replace() 規(guī)則相對復(fù)雜淌山,有很多玩法,了解更多 顾瞻。

反向引用

在正則表達(dá)式內(nèi)部引用之前(左側(cè))捕獲分組匹配的文本泼疑,形式如:\num ,其中 num 表示編號(hào)荷荤,編號(hào)規(guī)則與之前介紹的相同退渗。
舉個(gè)例子:
比如要匹配: 2018-12-302018.12.302018/12/30 三種形式蕴纳。
可能首先想到的是:\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}会油,但是:

var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
var text = '2018-12.30';
console.log(regex.test(text));  // true

顯然,我們不希望匹配 2018-12.30 古毛,我們需要前后的分隔符相同:

var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var text1 = '2018-12.30';
var text2 = '2018-12-30';
var text3 = '2018/12/30';

console.log(regex.test(text1));  // false
console.log(regex.test(text2));  // true
console.log(regex.test(text3));  // true

這里的 \1 就是對前面 (-|\/|\.) 的引用翻翩,表達(dá)式可視化如下:

反向引用可視化

反向引用的二義性:

在反向引用中都许,如果編號(hào)大于9就會(huì)出現(xiàn)二義性,如:\10 是表示第十個(gè)捕獲分組呢還是表示第一個(gè)捕獲分組和一個(gè)字符 0 呢嫂冻?
在一些編程語言中有專門的規(guī)定來避免二義性胶征,但是在JavaScript中并沒有,JavaScript對于 \10 的處理是:

  1. 如果存在第 10 個(gè)捕獲分組桨仿,則引用對應(yīng)的分組
  2. 如果不存在睛低,則引用 \1

如果,在有第 10 個(gè)捕獲分組的情況下服傍,要匹配 \1 和 字符0 的話钱雷,可以使用下面兩種方法:

  • 命名分組
  • 再使用括號(hào)將 \10 括起來,比如 (\1)0\1(?:0)

命名分組

由于按編號(hào)引用分組存在一些問題伴嗡,如:可讀性差急波,不易維護(hù)从铲,二義性等瘪校。于是出現(xiàn)了命名分組,使用易記憶名段,易辨別的名字來代替編號(hào)阱扬。
注意:命名分組是 ES2017 新特性。

語法規(guī)則如下:

  • 分組:(?<name>)
  • 提壬毂佟:$<name>
  • 反向引用:\k<name>

比如麻惶,上文的一個(gè)例子可以改為:

var regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
var text = '2018-12-30';
var result = text.replace(regex, '$<month>/$<day>/$<year>');

console.log(result);   // 12/30/2018

對于方法 String.prototype.match()RegExp.prototype.exec() 也有了新玩法:

var regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
var text = '2018-12-30';
var matchObj = text.match(regex);

console.log(matchObj.groups);
// {year: "2018", month: "12", day: "30"}

在匹配結(jié)果中,多了 groups 屬性信夫,保存了所有命名捕獲分組的匹配結(jié)果窃蹋。

再來看一個(gè)反向引用的例子:

var regex = /\d{4}(?<split>-|\/|\.)\d{2}\k<split>\d{2}/;
var text = '2018-12-30';

console.log(regex.test(text));  // true

非捕獲分組

括號(hào)的功能有“疊加”性。括號(hào)可以表示分組静稻,用來構(gòu)成單個(gè)元素警没;也可以表示多選結(jié)構(gòu);但同時(shí)振湾,也構(gòu)成了引用分組杀迹。
在僅僅需要標(biāo)記范圍(分組或多選結(jié)構(gòu))時(shí),正則表達(dá)式保存已經(jīng)匹配的文本會(huì)造成不必要的性能浪費(fèi)押搪。
這時(shí)候我們可以使用 非捕獲型括號(hào) (?:...)來限定分組或多選結(jié)構(gòu)的范圍:(?:p)(?:p1|p2)树酪。這種只用來限定范圍不捕獲匹配文本的分組就是 非捕獲分組

非捕獲型分組的優(yōu)點(diǎn)是性能好大州,缺點(diǎn)是不美觀续语,可讀性差。
在實(shí)際應(yīng)用中厦画,建議盡量使用非捕獲分組绵载。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子娃豹,更是在濱河造成了極大的恐慌焚虱,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懂版,死亡現(xiàn)場離奇詭異鹃栽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)躯畴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門民鼓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蓬抄,你說我怎么就攤上這事丰嘉。” “怎么了嚷缭?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵饮亏,是天一觀的道長。 經(jīng)常有香客問我阅爽,道長路幸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任付翁,我火速辦了婚禮简肴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘百侧。我一直安慰自己砰识,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布佣渴。 她就那樣靜靜地躺著辫狼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪观话。 梳的紋絲不亂的頭發(fā)上予借,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機(jī)與錄音频蛔,去河邊找鬼灵迫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晦溪,可吹牛的內(nèi)容都是我干的瀑粥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼三圆,長吁一口氣:“原來是場噩夢啊……” “哼狞换!你這毒婦竟也來了避咆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤修噪,失蹤者是張志新(化名)和其女友劉穎查库,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體黄琼,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡樊销,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脏款。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片围苫。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撤师,靈堂內(nèi)的尸體忽然破棺而出剂府,到底是詐尸還是另有隱情,我是刑警寧澤剃盾,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布腺占,位于F島的核電站,受9級特大地震影響万俗,放射性物質(zhì)發(fā)生泄漏湾笛。R本人自食惡果不足惜饮怯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一闰歪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓖墅,春花似錦库倘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贪壳,卻和暖如春饱亿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闰靴。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工彪笼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚂且。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓配猫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杏死。 傳聞我的和親對象是個(gè)殘疾皇子泵肄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 個(gè)人根據(jù)《正則指引》內(nèi)容總結(jié)記錄捆交,侵刪!腐巢! 轉(zhuǎn)載至我的博客 最近看了編譯原理方面的書品追,覺得正則表達(dá)式非常重要,在各...
    J退後閱讀 602評論 0 0
  • 初衷:看了很多視頻冯丙、文章诵盼,最后卻通通忘記了,別人的知識(shí)依舊是別人的银还,自己卻什么都沒獲得风宁。此系列文章旨在加深自己的印...
    DCbryant閱讀 4,009評論 0 20
  • 正則表達(dá)式有很多流派,也有很多的特性蛹疯,不同的語言支持度也是不一樣的戒财。本篇文章是寫Python中的正則表達(dá)式的用法的...
    Moscow1147閱讀 1,097評論 0 0
  • 從匹配中返回值 Match 對象 成功的匹配總是返回一個(gè) Match 對象, 這個(gè)對象通常也被放進(jìn) $/ 中, (...
    焉知非魚閱讀 1,800評論 0 1
  • 專業(yè)考題類型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項(xiàng)A選項(xiàng)B選項(xiàng)C選項(xiàng)D選項(xiàng)E選項(xiàng)F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 8,986評論 0 13