正則表示式的先行斷言與後行斷言
正則表示式的先行斷言和後行斷言一共有4種形式:
(?=pattern) 零寬正向先行斷言(zero-width positive lookahead assertion)
(?!pattern) 零寬負向先行斷言(zero-width negative lookahead assertion)
(?<=pattern) 零寬正向後行斷言(zero-width positive lookbehind assertion)
(?<!pattern) 零寬負向後行斷言(zero-width negative lookbehind assertion)
這裡面的pattern是一個正則表示式。
如同^代表開頭,$代表結尾,\b代表單詞邊界一樣,先行斷言和後行斷言也有類似的作用,它們只匹配某些位置,在匹配過程中,不佔用字元,所以被稱為“零寬”。所謂位置,是指字串中(每行)第一個字元的左邊、最後一個字元的右邊以及相鄰字元的中間(假設文字方向是頭左尾右)。
下面分別舉例來說明這4種斷言的含義。
(?=pattern) 正向先行斷言
代表字串中的一個位置,緊接該位置之後的字元序列能夠匹配pattern。
例如對”a regular expression”這個字串,要想匹配regular中的re,但不能匹配expression中的re,可以用”re(?=gular)”,該表示式限定了re右邊的位置,這個位置之後是gular,但並不消耗gular這些字元,將表示式改為”re(?=gular).”,將會匹配reg,元字元.匹配了g,括號這一砣匹配了e和g之間的位置。
(?!pattern) 負向先行斷言
代表字串中的一個位置,緊接該位置之後的字元序列不能匹配pattern。
例如對”regex represents regular expression”這個字串,要想匹配除regex和regular之外的re,可以用”re(?!g)”,該表示式限定了re右邊的位置,這個位置後面不是字元g。負向和正向的區別,就在於該位置之後的字元能否匹配括號中的表示式。
(?<=pattern) 正向後行斷言
代表字串中的一個位置,緊接該位置之前的字元序列能夠匹配pattern。
例如對”regex represents regular expression”這個字串,有4個單詞,要想匹配單詞內部的re,但不匹配單詞開頭的re,可以用”(?<=\w)re”,單詞內部的re,在re前面應該是一個單詞字元。之所以叫後行斷言,是因為正則表示式引擎在匹配字串和表示式時,是從前向後逐個掃描字串中的字元,並判斷是否與表示式符合,當在表示式中遇到該斷言時,正則表示式引擎需要往字串前端檢測已掃描過的字元,相對於掃描方向是向後的。
(?<!pattern) 負向後行斷言
代表字串中的一個位置,緊接該位置之前的字元序列不能匹配pattern。
例如對”regex represents regular expression”這個字串,要想匹配單詞開頭的re,可以用”(?<!\w)re”。單詞開頭的re,在本例中,也就是指不在單詞內部的re,即re前面不是單詞字元。當然也可以用”\bre”來匹配。
對於這4個斷言的理解,可以從兩個方面入手:
1.關於先行(lookahead)和後行(lookbehind):正則表示式引擎在執行字串和表示式匹配時,會從頭到尾(從前到後)連續掃描字串中的字元,設想有一個掃描指標指向字元邊界處並隨匹配過程移動。先行斷言,是當掃描指標位於某處時,引擎會嘗試匹配指標還未掃過的字元,先於指標到達該字元,故稱為先行。後行斷言,引擎會嘗試匹配指標已掃過的字元,後於指標到達該字元,故稱為後行。
2.關於正向(positive)和負向(negative):正向就表示匹配括號中的表示式,負向表示不匹配。
對這4個斷言形式的記憶:
1.先行和後行:後行斷言(?<=pattern)、(?<!pattern)中,有個小於號,同時也是箭頭,對於自左至右的文字方向,這個箭頭是指向後的,這也比較符合我們的習慣。把小於號去掉,就是先行斷言。
2.正向和負向:不等於(!=)、邏輯非(!)都是用!號來表示,所以有!號的形式表示不匹配、負向;將!號換成=號,就表示匹配、正向。
我們經常用正則表示式來檢測一個字串中包含某個子串,要表示一個字串中不包含某個字元或某些字元也很容易,用[^...]形式就可以了。要表示一個字串中不包含某個子串(由字元序列構成)呢?
用[^...]這種形式就不行了,這時就要用到(負向)先行斷言或後行斷言、或同時使用。
例如判斷一句話中包含this,但不包含that。
包含this比較好辦,一句話中不包含that,可以認為這句話中每個字元的前面都不是that或每個字元的後面都不是that。正則表示式如下:
^((?<!that).)*this((?<!that).)*$ 或 ^(.(?!that))*this(.(?!that))*$
對於”this is the case”這句話,兩個表示式都能夠匹配成功,而”note that this is the case”都匹配失敗。
在一般情況下,這兩個表示式基本上都能夠滿足要求了。考慮極端情況,如一句話以that開頭、以that結尾、that和this連在一起時,上述表示式就可能不勝任了。
如”note thatthis is the case”或者”this is the case, not that”等。
只要靈活運用這幾個斷言,就很容易解決:
^(.(?<!that))*this(.(?<!that))*$
^(.(?<!that))*this((?!that).)*$
^((?!that).)*this(.(?<!that))*$
^((?!that).)*this((?!that).)*$
這4個正則表示式測試上述的幾句話,結果都能夠滿足要求。
上述4種斷言,括號裡的pattern本身是一個正則表示式。但對2種後行斷言有所限制,在Perl和Python中,這個表示式必須是定長(fixed length)的,即不能使用*、+、?等元字元,如(?<=abc)沒有問題,但(?<=a*bc)是不被支援的,特別是當表示式中含有|連線的分支時,各個分支的長度必須相同。之所以不支援變長表示式,是因為當引擎檢查後行斷言時,無法確定要回溯多少步。Java支援?、{m}、{n,m}等符號,但同樣不支援*、+字元。Javascript乾脆不支援後行斷言,不過一般來說,這不是太大的問題。參見這裡。
試驗例子
sole sorry chilly high tight laughter
匹配h
匹配後面是t的h
匹配後面不是t的h
匹配前面是g的h
匹配前面不是g的h
先行斷言和後行斷言某種程度上就好比使用if語句對匹配的字元前後做判斷驗證。
相關推薦
正則表示式的先行斷言與後行斷言
正則表示式的先行斷言和後行斷言一共有4種形式: (?=pattern) 零寬正向先行斷言(zero-width positive lookahead assertion) (?!pattern) 零寬負向先行斷言(zero-width negative lookahe
js中利用正則表示式實現空格與換行的互相轉換
1.將換行符轉換成空格: var content = accountInfo.replace(/\r\n/mg,' '); 2.將空格轉換成換行:var accountInfo = (document.getElementById("accountInfo").value
正則表示式的先行斷言、後行斷言
實際開發中經常會遇到這樣的情況,需要在一組字串中取出指定的字元,例如:取出如下一段程式碼裡和html標籤<div><h2>title</h2><p>content</p></div>理想的結果是:['di
先行斷言和後行斷言
script 字符串 pre ring ctu lac them 引擎 have 後行斷言 JavaScript 語言的正則表達式,只支持先行斷言(lookahead)和先行否定斷言(negative lookahead),不支持後行斷言(lookbehind)和後行否定斷
java中正則表示式的瞭解與實踐記錄
Pattern pattern = Pattern.compile("^\\S+/q/"); Matcher matcher = pattern.matcher(resultString); String qrcode=""; while(matcher.find()){ String path
linux 正則表示式 之 sed 與awk
sed 可以將資料進行替換,刪除,新增,選取等操作 sed [引數] [動作] 引數: -n :使用安靜模式 ,只有經過sed特殊處理的那一行才能被列出來 -i : 直接修改讀取檔案內容,而不是由螢幕輸出 -e
正則表示式的效率與優化
以下內容整理自《PHP核心技術與最佳實踐》 一、使用字元組代替分支條件 eg. 使用[a-d]表示a~d之間的字母,而不是使用(a|b|c|d) function regTest($pattern,$str,$cnt){ $start=mi
Java正則表示式替換移除空行和多餘的空格
這幾天重拾Java寫程式碼,需要操作文字檔案中的內容。 最終,要把內容裡的空行和多餘的連續空格移除,使用String裡的replace或者replaceAll,試了很多次都沒有成功。 最後發現需要使用正則表示式Regex。先把解決方案共享如下。 1. 移除多餘的連續空格,只
javascripts使用正則表示式--1概念與定義
正則表示式(regular Expression)是一個描述字元模式的物件,其中字元模式由普通字元(如A~Z 、a~z、0~9)和特殊字元(元字元)組成 一、 正則表示式的運用範圍: 1、驗證字串:驗證給定的字串或子字串是否符合指定特徵,例如,驗證郵件地址、電話號碼、手機號碼等
Java正則表示式的語法與示例
正向肯定預查,在任何匹配pattern的字串開始處匹配查詢字串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。預查不消耗字元
正則表示式之貪婪與非貪婪模式(II)
貪婪模式 正則表示式在匹配的時候會盡可能多的匹配,直至匹配失敗 如: '123456789'.replace(/\d{3,7}/g,'X') 結果: "X89" 非貪婪模式 讓正
JMeter 正則表示式提取器(後置處理器取值)
JMeter的測試中,經常遇到上一步驟中生成的資料需要在下一個程式中使用到,這個時候,我們就需要在上一程式的頁面中取得生成的值。 我們經常使用正則表示式提取器來進行處理。 例1如下: 引用名稱: tokenid(自己定義) 正則表示式:<
Linux正則表示式-選擇性操作與分組操作
選擇性操作 豎線(|)字元是元字元擴充套件集的一部分,用於指定正則表示式的聯合。如果某行匹配其中的一個正則表示式,那麼它就匹配該模式。例如,正則表示式:UNIX|LINUX 將匹配包含字串"UNIX" 或字串"LINUX" 的行。可以指定更多的選擇,例如: UNIX|LI
[正則表示式] 預搜尋(零寬斷言)詳解
什麼是零寬斷言? 零寬斷言的意思是(匹配寬度為零,滿足一定的條件/斷言) 我也不知道這個詞語是那個王八蛋發明的,簡直是太拗口了。 零寬斷言用於查詢在某些內容(但並不包括這些內容)之前或之後的東西,也就是說它們像 \b ^ $ \< \> 這樣的錨定作用,僅僅用
javascipt 正則表示式 匹配 單行和多行註釋
在使用node.js時。如果我們使用.json檔案儲存一些配置時,希望加上一些註釋。 但是由於讀取時,是讀取字串 ,然後用JSON.parse 來 轉換成json物件,由於有註釋的存在則無法正確轉換甚至報錯。 一下正則表示式 匹配字串中的所以註釋,包括單行和多行註釋 (
正則表示式 日常使用 匹配空行 刪除指定文字所在行 ...
正則表示式匹配空行測試所用的編輯器:notepad++^(\s*)\r\n根據文件格式(windows, mac, linux行尾符)不同 將其中的\r\n替換成不同行尾符windows: ^(\s*)\r\nlinux: ^(\s*)\nmac: ^(\s*)\r (沒用過
Python3-正則表示式~貪婪模式與非貪婪模式
import re str1 = r'aa<div>test1</div>bb<div>test2</div>cc' str2 = r'aa<div>test1<div>test2</div>
正則表示式之--貪婪與非貪婪模式詳解
“.*”取得控制權後,由A後面的位置開始嘗試匹配,由於是貪婪模式,優化嘗試匹配,一直匹配到字串的結束位置,將控制權交給“"”。“"”取得控制權後,由於已經是字串的結束位置,匹配失敗,查詢可供回溯的狀態,將控制權交給“.*”,由“.*”讓出已匹配字元“.”。重複以上過程,直到後面“"”匹配了C處後面的字元“””
python正則表示式匹配時間與日期
import refrom datetime import datetimetest_date = '他的生日是2016-12-12 14:34,是個可愛的小寶貝.二寶的生日是2016-12-21 11:34,好可愛的.'test_datetime = '他的生日是2016-
正則表示式的貪婪與非貪婪
var s='120000|天津市,130000|河北省,210000|遼寧省,220000|吉林省,310000|上海市,320000|江蘇省,330000|浙江省,'; var r = /310000/|(.*?)(?:,)/ r.exec(s); s.match(