正則表達式斷言


title: 正則表達式斷言
tags: [正則表達式]
date: 2017-11-15 23:55:55


正則表達式大多數(shù)結(jié)構(gòu)匹配的文本會出現(xiàn)在最終的匹配結(jié)果中,但也有些結(jié)構(gòu)并不真正匹配文本唯欣,而只是負責(zé)判斷某個位置左/右側(cè)是否符合要求聘芜,這種結(jié)構(gòu)被稱為斷言(assertion)。常見的斷言有三類: 單詞邊界除秀、行起始/結(jié)束位置、環(huán)視。本文主要簡單闡述對三類斷言的理解瓢剿。

單詞邊界

單詞邊界顧名思義,是指單詞字符(\w)能匹配的字符串的左右位置悠轩。在javascript间狂、php、python 2火架、ruby中鉴象,單詞字符(\w)等同于[0-9a-zA-Z],所以在這些語言中忙菠,給定一段文本可以用\b\w+\b把所有單詞提取出來。

// 例如
('Love is composed of a single soul inhabiting two bodies.').match(/\b\w+\b/g)

return ["Love", "is", "composed", "of", "a", "single", "soul", "inhabiting", "two", "bodies"]

這里值得注意的是纺弊,有些單詞例如e-mail和組合詞I'm這樣的牛欢,\b\w+\b是無法匹配的。如要匹配淆游,可根據(jù)需求修改為\b['-\w]\b

單詞邊界記為\b傍睹,它能匹配的位置:一邊是單詞字符\w,一邊是非單詞字符\W犹菱。
與單詞邊界對應(yīng)的是非單詞邊界\B焰望,兩者關(guān)系類似\w\W\d\D已亥。

這里注意熊赖,非單詞邊界(\B)和單詞字符(\w)是不一樣的,因為前者是斷言虑椎,而后者是普通匹配震鹉。例如:

// 式一
String(1234567890).replace(/(?=(\B)(\d{3})+$)/g, ',')
=> 1,234,567,890
// 式二
String(1234567890).replace(/(?=(\w)(\d{3})+$)/g, ',')
=> ,123,456,7890
// 附加常用例子,20180911格式化為2018-09-11
'20180911'.replace(/(?=\B(\d{2})+$)/g, '-').replace(/-/, '')
=>2018-09-11

造成差異的原因就是:

式一中的\B匹配邊界(是斷言)捆姜。第一次匹配時传趾,在1234567890中數(shù)字1的前方時,會環(huán)視后方進行肯定斷言(?=):后方必須是滿足兩個pattern才通過泥技。第一個pattern(\B)在數(shù)字1的前方匹配成功浆兰;故繼續(xù)在此位置匹配第二個pattern(\d{3})+$,發(fā)現(xiàn)123456789之后并不是結(jié)束符(結(jié)束符和開始符也是斷言珊豹,下文講述)簸呈,故匹配失敗。開始第二次匹配店茶,從數(shù)字1和數(shù)字2的中間開始...最后會匹配成功三個位置:1和2之間蜕便、4和5之間、7和8之間贩幻,再被,替換轿腺,故得到結(jié)果。

同理丛楚,式二在第一次匹配時族壳,在數(shù)字1的前方環(huán)視后方進行肯定斷言:后方必須是滿足兩個pattern才通過。第一個pattern(\w)在數(shù)字1的前方匹配成功趣些,并將匹配位置移動到1和2之間仿荆;然后繼續(xù)匹配第二個pattern(\d{3})+$...第一次匹配成功,故數(shù)字1前方的斷言是成功的,標(biāo)記該位置...最后得到三個位置:1前方赖歌、3和4之間枉圃、6和7之間,再被,替換庐冯,故得到結(jié)果孽亲。

所以\B只是去判斷該位置左右是否只有一邊有單詞字符,另一邊不是單詞字符展父,且在匹配成功時返劲,不會導(dǎo)致匹配位置發(fā)生改變。說起來算是一種判斷吧~

這種只是匹配某個位置而不是文本的元字符栖茉,在正則中也被稱為錨點篮绿。下文繼續(xù)介紹常見錨點之二:行起始/結(jié)束位置

行起始/結(jié)束位置

^$分別表示(行)起始位置和(行)結(jié)束位置,比如正則表達式/^lu.*r$/只能匹配的lu開始并以r結(jié)束的字符串吕漂,例如:luwuer亲配、lu fd --r,不能匹配nb luwuer惶凝、lu fd --rb等吼虎。

其實行起始/結(jié)束位置斷言,常用在正則表達式開啟多行模式(Multiline Mode)的情況下苍鲜。例如:

注:js開啟多行模式的方式思灰,在正則表達式后添加附加參數(shù)m,同全局匹配g

('first line\nsecond line\nlast line').match(/^\w+/gm)
return ["first", "second", "last"]

既然是多行匹配混滔,這里說說如何劃分洒疚。

在編輯文本時,敲回車鍵就向文本輸入了行終止符(line terminal)坯屿,表示結(jié)束當(dāng)前行油湖。這里只需注意,敲入回車時向文本中輸入的行終止符在主流平臺上是有差別的:

  • Windows的行終止符是\r\n
  • UNIX/Linux/Mac OS的行終止符是\n

不過正則的行起始/結(jié)束位置斷言都是可以識別的哈~

環(huán)視

環(huán)視是指在某個位置向左/向右看愿伴,保證其左/右位置必須出現(xiàn)某類字符(包括單詞字符\w和非單詞字符\W)肺魁,且環(huán)視也同上兩個斷言电湘,只是做一個判斷(匹配一個位置隔节,本身不匹配任何字符,但又比上兩個斷言靈活)寂呛。也有人稱環(huán)視為零寬斷言怎诫。

環(huán)視分為四種:

  • 肯定順序環(huán)視(正向肯定斷言)positive-lookahead: ?=pattern
  • 否定順序環(huán)視(正向否定斷言)positive-lookahead: ?!pattern
  • 肯定逆序環(huán)視(反向肯定斷言)positive-lookahead: ?<=pattern,ES2018支持
  • 否定逆序環(huán)視(反向否定斷言)positive-lookahead: ?<!pattern贷痪,ES2018支持

逆序環(huán)視兼容性:https://caniuse.com/?search=%20regular%20expressions%20lookbehind

逆序環(huán)視兼容性

比如我們要匹配一串文字中包含在書名號《》中的書名幻妓,如不考慮環(huán)視可能需要如下實現(xiàn):

('三體是劉慈欣創(chuàng)作的系列長篇科幻小說,由《三體》劫拢、《三體Ⅱ·黑暗森林》肉津、《三體Ⅲ·死神永生》組成强胰。').match(/《.*?》/g).join(',').replace(/[《》]/g, '').split(',')
return ["三體", "三體Ⅱ·黑暗森林", "三體Ⅲ·死神永生"]

正則默認是婪模模式(在整個表達式匹配成功的前提下,盡可能多的匹配)妹沙,開啟非貪婪模式(在整個表達式匹配成功的前提下偶洋,盡可能少的匹配)的方法:在貪婪量詞{m,n}{m,}距糖、?玄窝、*+后加上一個?號悍引,例如+?

而在使用環(huán)視時會更簡單:

('三體是劉慈欣創(chuàng)作的系列長篇科幻小說恩脂,由《三體》、《三體Ⅱ·黑暗森林》趣斤、《三體Ⅲ·死神永生》組成俩块。').replace(/《/g,'\n').match(/^.*?(?=》)/gm)
return ["三體", "三體Ⅱ·黑暗森林", "三體Ⅲ·死神永生"]

hah,例子沒舉好浓领,似乎也沒簡單多少...當(dāng)然最主要的原因是js不支持逆序環(huán)視啦啦啦

再舉例典阵,匹配6位數(shù)字構(gòu)成的字符串:

// 無環(huán)視
'http://luwuer.com/629212/1234567890'.match(/[^\d]\d{6}[^\d]/g).join('').match(/\d{6}/g)
return ["629212"]
// 環(huán)視
'http://luwuer.com/629212/1234567890'.match(/(?!\d).\d{6}(?!\d)/g).join('').match(/\d{6}/g)
return ["629212"]

其實環(huán)視在js中更多的是與replace函數(shù)組合,就像在單詞邊界一節(jié)中最后的例子镊逝。

  • 原文 不要誤會壮啊,就是我寫的 /keai
  • 參考《正則指引》 - 于晟
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撑蒜,隨后出現(xiàn)的幾起案子歹啼,更是在濱河造成了極大的恐慌,老刑警劉巖座菠,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狸眼,死亡現(xiàn)場離奇詭異,居然都是意外死亡浴滴,警方通過查閱死者的電腦和手機拓萌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來升略,“玉大人微王,你說我怎么就攤上這事∑废” “怎么了炕倘?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翰撑。 經(jīng)常有香客問我罩旋,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任涨醋,我火速辦了婚禮瓜饥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浴骂。我一直安慰自己压固,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布靠闭。 她就那樣靜靜地躺著帐我,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愧膀。 梳的紋絲不亂的頭發(fā)上拦键,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音檩淋,去河邊找鬼芬为。 笑死,一個胖子當(dāng)著我的面吹牛蟀悦,可吹牛的內(nèi)容都是我干的媚朦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼日戈,長吁一口氣:“原來是場噩夢啊……” “哼询张!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浙炼,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤份氧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弯屈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜗帜,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年资厉,在試婚紗的時候發(fā)現(xiàn)自己被綠了厅缺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宴偿,死狀恐怖湘捎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酪我,我是刑警寧澤消痛,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站都哭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欺矫,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一纱新、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧穆趴,春花似錦脸爱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至络它,卻和暖如春族檬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背化戳。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工单料, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人点楼。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓扫尖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掠廓。 傳聞我的和親對象是個殘疾皇子换怖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

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