零寬斷言是正則表達(dá)式中的一種方法,正則表達(dá)式在計(jì)算機(jī)科學(xué)中,是指一個(gè)用來描述或者匹配一系列符合某個(gè)句法規(guī)則的字符串的單個(gè)字符串。
定義解釋
零寬斷言是正則表達(dá)式中的一種方法
正則表達(dá)式在計(jì)算機(jī)科學(xué)中,是指一個(gè)用來描述或者匹配一系列符合某個(gè)句法規(guī)則的字符串的單個(gè)字符串。在很多文本編輯器或其他工具里,正則表達(dá)式通常被用來檢索和/或替換那些符合某個(gè)模式的文本內(nèi)容。許多程序設(shè)計(jì)語言都支持利用正則表達(dá)式進(jìn)行字符串操作。例如,在Perl中就內(nèi)建了一個(gè)功能強(qiáng)大的正則表達(dá)式引擎。正則表達(dá)式這個(gè)概念最初是由Unix中的工具軟件(例如sed和grep)普及開的。正則表達(dá)式通??s寫成“regex”,單數(shù)有regexp、regex,復(fù)數(shù)有regexps、regexes、regexen。
零寬斷言
用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或之后的東西,也就是說它們像/b,^,$那樣用于指定一個(gè)位置,這個(gè)位置應(yīng)該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言。最好還是拿例子來說明吧: 斷言用來聲明一個(gè)應(yīng)該為真的事實(shí)。正則表達(dá)式中只有當(dāng)斷言為真時(shí)才會(huì)繼續(xù)進(jìn)行匹配。
(?=exp)也叫零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp。比如/b(?=re)/w+/b,匹配以re開頭的單詞的后面部分(除了re以外的部分),如查找reading a book.時(shí),它會(huì)匹配ading。
var reg = new Regex(@"/w+(?=ing)");var str = "muing";Console.WriteLine(reg.Match(str).Value);//返回mu
(?<=exp)也叫零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達(dá)式exp。比如/b/w+(?<=ing/b)會(huì)匹配以ing結(jié)尾的單詞的前半部分(除了ing以外的部分),例如在查找I am reading.時(shí),它匹配read。
假如你想要給一個(gè)很長的數(shù)字中每三位間加一個(gè)逗號(當(dāng)然是從右邊加起了),你可以這樣查找需要在前面和里面添加逗號的部分:((?=/d)/d{3})+/b,用它對1234567890進(jìn)行查找時(shí)結(jié)果是234567890。
下面這個(gè)例子同時(shí)使用了這兩種斷言:(?<=/s)/d+(?=/s)匹配以空白符間隔的數(shù)字(再次強(qiáng)調(diào),不包括這些空白符)。
負(fù)向零寬斷言
前面我們提到過怎么查找不是某個(gè)字符或不在某個(gè)字符類里的字符的方法(反義)。但是如果我們只是想要確保某個(gè)字符沒有出現(xiàn),但并不想去匹配它時(shí)怎么辦?例如,如果我們想查找這樣的單詞--它里面出現(xiàn)了字母q,但是q后面跟的不是字母u,我們可以嘗試這樣:
/b/w*q[^u]/w*/b匹配包含后面不是字母u的字母q的單詞。但是如果多做測試(或者你思維足夠敏銳,直接就觀察出來了),你會(huì)發(fā)現(xiàn),如果q出現(xiàn)在單詞的結(jié)尾的話,像Iraq,Benq,這個(gè)表達(dá)式就會(huì)出錯(cuò)。這是因?yàn)閇^u]總要匹配一個(gè)字符,所以如果q是單詞的最后一個(gè)字符的話,后面的[^u]將會(huì)匹配q后面的單詞分隔符(可能是空格,或者是句號或其它的什么),后面的/w*/b將會(huì)匹配下一個(gè)單詞,于是/b/w*q[^u]/w*/b就能匹配整個(gè)Iraq fighting。負(fù)向零寬斷言能解決這樣的問題,因?yàn)樗黄ヅ湟粋€(gè)位置,并不消費(fèi)任何字符。現(xiàn)在,我們可以這樣來解決這個(gè)問題:/b/w*q(?!u)/w*/b。
零寬度負(fù)預(yù)測先行斷言(?!exp),斷言此位置的后面不能匹配表達(dá)式exp。例如:/d{3}(?!/d)匹配三位數(shù)字,而且這三位數(shù)字的后面不能是數(shù)字;/b((?!abc)/w)+/b匹配不包含連續(xù)字符串a(chǎn)bc的單詞。
同理,我們可以用(?<!exp),零寬度負(fù)回顧后發(fā)斷言來斷言此位置的前面不能匹配表達(dá)式exp:(?<![a-z])/d{7}匹配前面不是小寫字母的七位數(shù)字。
一個(gè)更復(fù)雜的例子:(?<=<(/w+)>).*(?=<///1>)匹配不包含屬性的簡單HTML標(biāo)簽內(nèi)里的內(nèi)容。(<?=(/w+)>)指定了這樣的前綴:被尖括號括起來的單詞(比如可能是<b>),然后是.*(任意的字符串),最后是一個(gè)后綴(?=<///1>)。注意后綴里的//,它用到了前面提過的字符轉(zhuǎn)義;/1則是一個(gè)反向引用,引用的正是捕獲的第一組,前面的(/w+)匹配的內(nèi)容,這樣如果前綴實(shí)際上是<b>的話,后綴就是</b>了。整個(gè)表達(dá)式匹配的是<b>和</b>之間的內(nèi)容(再次提醒,不包括前綴和后綴本身)。
上面的看了有點(diǎn)傷腦筋啊。下面來點(diǎn)補(bǔ)充一
斷言用來聲明一個(gè)應(yīng)該為真的事實(shí)。正則表達(dá)式中只有當(dāng)斷言為真時(shí)才會(huì)繼續(xù)進(jìn)行匹配。
接下來的四個(gè)用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或之后的東西,也就是說它們像/b,^,$那樣用于指定一個(gè)位置,這個(gè)位置應(yīng)該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言。最好還是拿例子來說明吧:
(?=exp)
也叫零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp。比如/b/w+(?=ing/b),匹配以ing結(jié)尾的單詞的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.時(shí),它會(huì)匹配sing和danc。
(?<=exp)
也叫零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達(dá)式exp。比如(?<=/bre)/w+/b會(huì)匹配以re開頭的單詞的后半部分(除了re以外的部分),例如在查找reading a book時(shí),它匹配ading。
假如你想要給一個(gè)很長的數(shù)字中每三位間加一個(gè)逗號(當(dāng)然是從右邊加起了),你可以這樣查找需要在前面和里面添加逗號的部分:((?<=/d)/d{3})*/b,用它對1234567890進(jìn)行查找時(shí)結(jié)果是234567890。
下面這個(gè)例子同時(shí)使用了這兩種斷言:(?<=/s)/d+(?=/s)匹配以空白符間隔的數(shù)字(再次強(qiáng)調(diào),不包括這些空白符)。
補(bǔ)充二:
最近為了對html文件進(jìn)行源碼處理,需要進(jìn)行正則查找并替換。于是借著這個(gè)機(jī)會(huì)把正則系統(tǒng)地學(xué)一下,雖然以前也用過正則,但每次都是臨時(shí)學(xué)一下混過關(guān)的。在學(xué)習(xí)的過程中還是遇到不少問題的,特別是零寬斷言(這里還要吐槽下,網(wǎng)上到處都是都復(fù)制粘貼的內(nèi)容,遇到個(gè)問題查看了不少重復(fù)的東西,汗?。。。栽谶@里把自己的理解寫下來,方便以后查閱!
零寬度正預(yù)測先行斷言是什么呢,看msdn上的官方解釋定義
(?= 子表達(dá)式)
(零寬度正預(yù)測先行斷言。)僅當(dāng)子表達(dá)式在此位置的右側(cè)匹配時(shí)才繼續(xù)匹配。例如,/w+(?=/d) 與后跟數(shù)字的單詞匹配,而不與該數(shù)字匹配。
經(jīng)典的例子:某單詞以ing結(jié)尾,要獲取ing前面的內(nèi)容
var reg = new Regex(@"/w+(?=ing)");var str = "muing";Console.WriteLine(reg.Match(str).Value);//返回mu
以上是網(wǎng)上到處可見的例子,到這里或許你明白了,原來就是返回了exp表達(dá)式前面的內(nèi)容。
再看下面的的代碼
var reg = new Regex(@"a(?=b)c");var str = "abc";Console.WriteLine(reg.IsMatch(str));//返回false
為什么會(huì)返回false?
其實(shí)msdn官方定義已經(jīng)說了,只是它說得很官方而已。這里需要我們注意一個(gè)關(guān)鍵點(diǎn):此位置。沒錯(cuò),是位置而不是字符。那么結(jié)合官方定義和第一個(gè)例子來理解第二個(gè)例子:
因?yàn)閍后面是b,則此時(shí)返回了匹配內(nèi)容a(由第一個(gè)例子知道,只返回a不返回exp匹配的內(nèi)容),此時(shí)a(?=b)c中的a(?=b)部分已經(jīng)解決了,接下來要解決c的匹配問題了,此時(shí)匹配c要從字符串a(chǎn)bc哪里開始呢,結(jié)合官方定義,就知道是從子表達(dá)的位置向右開始的,那么就是從b的位置開始,但b又不匹配a(?=b)c剩余部分的c,所以abc就不匹配a(?=b)c了。
那么如果要上面的進(jìn)行匹配,正則應(yīng)該如何寫呢?
答案是:a(?=b)bc
當(dāng)然,有人會(huì)說直接abc就匹配上了,還要這么折騰嗎?當(dāng)然不用這么折騰,只是為了說明零寬度正預(yù)測先行斷言到底是怎么一回事?關(guān)于其它的零寬斷言也是同一原理!
補(bǔ)充三
(?=exp):零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp。
#匹配后面為_path,結(jié)果為product
'product_path'.scan /(product)(?=_path)/
(?<=exp):零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達(dá)式exp
#匹配前面為name:,結(jié)果為wangfei
'name:wangfei'.scan /(?<=name:)(wangfei)/ #wangfei
(?!exp):零寬度負(fù)預(yù)測先行斷言,斷言此位置的后面不能匹配表達(dá)式exp。
#匹配后面不是_path
'product_path'.scan /(product)(?!_path)/ #nil
#匹配后面不是_url
'product_path'.scan /(product)(?!_url)/ #product
(?<!exp):零寬度負(fù)回顧后發(fā)斷言來斷言此位置的前面不能匹配表達(dá)式exp
#匹配前面不是name:
'name:angelica'.scan /(?<!name:)(angelica)/ #nil
#匹配前面不是nick_name:
'name:angelica'.scan /(?<!nick_name:)(angelica)/#angelica
小編也受夠了這個(gè)東西,等有好的東西再分享,今天洗洗睡吧
新聞熱點(diǎn)
疑難解答
圖片精選