正則表達式

我的根據《正則指引》內容總結記錄,侵刪!!javascript

轉載至個人博客java

最近看了編譯原理方面的書,以爲正則表達式很是重要,在各個語言當中都有支持,因此總結了這篇文章,做爲學習總結以及記錄~python

正則表達式

Regular Expression 即描述某種規則的表達式。git

字符組

普通字符組

字符組(Character Class)是一組字符,表示 「在同一個位置可能出現的各類字符正則表達式

其寫法是在一對方括號[]之間列出全部可能出現的字符。markdown

#只要字符串中包含數字、字符就能夠匹配
re.search("[0123456789]","2") != None				#=>True
複製代碼

默認狀況下re.search(pattern,string)只判斷string的某個子串可否由pattern匹配,爲了測試整個string可否被pattern匹配,在pattern兩端加上^$。它們並不匹配任何字符,只表示「定位到字符串的起始位置」和「定位到字符串的結束位置」。oop

#使用^和$測試string被pattern完整匹配
#只要字符串中包含數字、字符就能夠匹配
re.search("[0123456789]","2") != None 				#=>True
re.search("[0123456789]","a2") != None 				#=>True
#整個字符串就是一個數字字符,才能夠匹配
re.search("^[0123456789]$","2") != None				#=>True
re.search("^[0123456789]$","12") != None			#=>False
複製代碼

字符組中的字符排列順序並不影響字符組的功能,出現重複字符也不影響,可是並不推薦在字符組中出現重複字符。性能

例如上例中匹配數字就要把全部數字都列出來仍是有些繁瑣,爲此正則表達式提供了範圍表示法range),它更直觀,能進一步簡化字符組。學習

在字符組中-表示範圍,通常是根據字符對應的碼值(Code Point)也就是字符在對應碼錶中的編碼數值來肯定的。小的在前,大的在後,因此[0-9]正確,而[9-0]會報錯。測試

在字符組中能夠同時並列多個-範圍表示法

#[0-9a-fA-F]準確判斷十六進制字符
re.search("^[0-9a-fA-F]$","0") != None				#=>True
re.search("^[0-9a-fA-F]$","c") != None				#=>True
re.search("^[0-9a-fA-F]$","i") != None				#=>True
複製代碼

還能夠用轉義序列\xhex來表示一個字符,其中\x是固定前綴。字符組中有時會出現這種表示法,它能夠表現一些難以輸入或者難以顯示的字符。依靠這種表示法能夠很方便的匹配全部的中文字符。

#[\x00-\x7F]準確判斷ASCII字符
re.search("^[\x00-\x7F]$","c") != None				#=>True
re.search("^[\x00-\x7F]$","I") != None				#=>True
re.search("^[\x00-\x7F]$","<") != None				#=>True
re.search("^[\x00-\x7F]$","0") != None				#=>True
複製代碼

元字符與轉義

字符組中的-並不能匹配橫線字符,這類字符叫作元字符[]^$都算元字符。

若是-緊鄰字符組中的[那麼它就是普通字符,其餘狀況都是元字符。

取消特殊含義的作法是在元字符前加上反斜槓\

#做爲普通字符
re.search("^[-09]$","3") != None					#=>False
re.search("^[-09]$","-") != None					#=>True
#做爲元字符
re.search("^[0-9]$","3") != None					#=>True
re.search("^[0-9]$","-") != None					#=>False
#轉義以後做爲普通字符
re.search("^[0\\-9]$","3") != None					#=>False
re.search("^[0\\-9]$","-") != None					#=>True
複製代碼

這段例子中,正則表達式是以字符串的方式傳入的,而字符串自己也有關於轉義的規定,因此要加兩個反斜槓\\

針對這種問題Python提供了原生字符串(Raw String),不須要考慮正則表達式以外的轉義(只有雙引號是例外,必須轉義成\")。

#原生字符串的使用
r"^[0\-9]$" == "^[0\\-9]$"			                        #=>True
#原生字符串的轉義要簡單許多
re.search(r"^[0\-9]$","3") != None					#=>False
re.search(r"^[0\-9]$","-") != None					#=>True

#]出現的位置不一樣含義不一樣
#未轉義的]
re.search(r"^[012]345$","2345") != None	                                #=>True
re.search(r"^[012]345]$","5") != None                                   #=>False
re.search(r"^[012]345]$","]") != None                                   #=>False
#轉義的]
re.search(r"^[012\]345]$","2345") != None                               #=>False
re.search(r"^[012\]345]$","5") != None                                  #=>True
re.search(r"^[012\]345]$","]") != None                                  #=>True
複製代碼

請注意,只有開方括號[須要轉義,閉方括號]不用。

#取消其餘元字符的特殊含義
re.search(r"^[012]345]$","3") != None                                   #=>False
re.search(r"^[012\\]345]$","3") != None                                 #=>False
re.search(r"^[012]$","[012]") != None                                   #=>False
re.search(r"^\[012]$","[012]") != None                                  #=>True
複製代碼

排除型字符組

排除型字符組(Negated Character Class)只是在**方括號[以後緊跟一個脫字符^**,因此[^0-9]表示0-9以外的字符,也就是「非數字字符」。

#使用排除型字符組
re.search(r"^[^0-9][0-9]$","A8") != None                                #=>True
re.search(r"^[^0-9][0-9]$","x6") != None                                #=>True

#排除型字符組必須匹配一個字符
re.search(r"^[0-9][0-9]$","8") != None                                  #=>False

#排除型字符組中,緊跟在"^"以後的一個"-"不是元字符
#匹配一個"-"、"0"、"9"以外的字符
re.search(r"^[^0-9]$","-") != None					#=>False
re.search(r"^[^-09]$","8") != None					#=>True
複製代碼

在排除型字符組中,^是一個元字符,但只有它緊跟在[以後時纔是元字符,若是想表示這個字符組中能夠出現^字符,不要讓它緊挨着[,不然要轉義。

#匹配4個字符之一:"0","^","1","2"
re.search(r"^[0^12]$","^") != None					#=>True
#"^"緊跟在"["以後,但通過轉義變爲普通字符,等於上一個表達式,不推薦。
re.search(r"^[\^012]$","^") != None					#=>True
複製代碼

字符組的簡記法

字符組間記法(shorthands):對於經常使用的表示數字字符、小寫字母這類字符組提供的簡單記法。

常見的有\d\w\s,其中\d等價於[0-9]d表明「數字(digit)」;\w等價於[0-9a-zA-Z_]w表明「單詞(word)」;\s等價於[ \t\r\n\v\f](第一個字符是空格),s表明「空白字符(space)」。(這些等價前提是採用ASCII匹配規則,採用Unicode匹配規則就不對了)。

#若是沒有原聲字符串\d就必須寫做\\d
re.search(r"^\d$","8") != None						#=>True
re.search(r"^\d$","a") != None						#=>False
re.search(r"^\w$","8") != None						#=>True
re.search(r"^\w$","a") != None						#=>True
re.search(r"^\w$","_") != None						#=>True
re.search(r"^\s$"," ") != None						#=>True
re.search(r"^\s$","\t") != None						#=>True
re.search(r"^\s$","\n") != None						#=>True
複製代碼

\w能匹配下劃線_

#字符組簡記法與普通字符組混用
#用在普通字符組內部
re.search(r"^[\da-zA-Z]$","8") != None                                  #=>True
re.search(r"^[\da-zA-Z]$","a") != None                                  #=>True
re.search(r"^[\da-zA-Z]$","c") != None                                  #=>True

#用在排除型字符組內部
re.search(r"^[^\w]$","8") != None					#=>False
re.search(r"^[^\w]$","_") != None					#=>False
re.search(r"^[^\w]$",",") != None					#=>True
複製代碼

相對於\d\w\s這三個普通字符組簡記法,正則表達式也提供了對應的排除型字符組的簡記法:\D\W\S——字母徹底同樣,只是改成大寫。

這些簡記法匹配字符互補:\s能匹配的字符,\S必定不能匹配,其餘同理。

#\d和\D
re.search(r"^\d$","8") != None						#=>True
re.search(r"^\d$","a") != None						#=>False
re.search(r"^\D$","8") != None						#=>False
re.search(r"^\D$","a") != None						#=>True

#\w和\W
re.search(r"^\w$","c") != None						#=>True
re.search(r"^\w$","!") != None						#=>False
re.search(r"^\W$","c") != None						#=>False
re,search(r"^\W$","!") != None						#=>True

#\s和\S
re.search(r"^\s$","\t") != None						#=>True
re.search(r"^\s$","0") != None						#=>False
re.search(r"^\S$","\t") != None						#=>False
re.search(r"^\S$","0") != None						#=>True
複製代碼

量詞

通常形式

字符組只能匹配單個字符,爲此正則表達式提供了量詞(quantifier),來支持匹配多個字符的功能。

#重複肯定次數的量詞
re.search(r"^\d{6}$","100859") != None				#=>True
re.search(r"^\d{6}$","20103") != None				#=>False
複製代碼

量詞還能夠表示不肯定的長度,其通用形式是{m,n},其中mn是兩個數字(逗號以後毫不能有空格),它限定以前的元素可以出現的次數,m是下限,n是上限(均爲閉區間)。若是不肯定長度的上限,也能夠省略,寫成\d{m,}。量詞限定通常都有明確的下限,若是沒有,則默認爲0。有些語言支持{,n}的記法,省略下限爲0的狀況,但這種用法並非全部語言都通用的,最好使用{0,n}的記法

量詞 說明
{n} 以前的元素必須出現n次
{m,n} 以前的元素最少出現m次,最多出現n次
{m,} 以前的元素最少出現m次,出現次數無上限
{0,n} 以前的元素能夠不出現,也能夠出現,最多出現n次(在某些語言中能夠寫爲{,n})
#表示不肯定長度的量詞 
re.search(r"^\d{4,6}$","123") != None				#=>False
re.search(r"^\d{4,5}$","1234") != None				#=>True
re.search(r"^\d{4,6}$","123456") != None			#=>True
re.search(r"^\d{4,6}$","1234567") != None			#=>False
re.search(r"^\d{4,}$","123") != None				#=>False
re.search(r"^\d{4,}$","1234") != None				#=>True
re.search(r"^\d{4,}","123456") != None				#=>True
re.search(r"^\d{0,6}$","12345") != None				#=>True
re.search(r"^\d{0,6}$","123456") != None			#=>True
re.search(r"^\d{0,6}$","1234567") != None			#=>False
複製代碼

經常使用量詞

{m,n}是通用形式的量詞,正則表達式還有三個經常使用量詞,分別是+?*。它們形態雖然不一樣於{m,n},功能卻相同。(能夠理解爲「量詞簡記法」)

經常使用量詞 {m,n}等價形式 說明
* {0,} 可能出現,也可能不出現,出現次數沒有上限
+ {1,} 至少出現1次,出現次數沒有上限
? {0,1} 至多出現1次,也可能不出現
#量詞?的應用
re.search(r"^travell?er$","traveler") != None                           #=>True
re.search(r"^travell?er$","traveller") != None                          #=>True

#量詞+的應用
re.search(r"^<[^>]+>$","<bold>") != None                                #=>True
re.search(r"^<[^>]+>$","</table>") != None                              #=>True
re.search(r"^<[^>]+>$","<>") != None                                    #=>False

#量詞*的使用
re.search(r"^\"[^\"]*\"$","\"some\"") != None                           #=>True
re.search(r"^\"[^\"]*\"$","\"\"") != None                               #=>True
複製代碼

點號

通常文檔都說點號能夠匹配「任意字符」,可是換行符\n不能匹配,若是非要匹配"任意字符",有兩種辦法:可使用單行匹配;或者使用[\s\S](也可使用[\w\W][\d\D])。

#點號.的匹配
re.search(r"^.$","a") != None						#=>True
re.search(r"^.$","0") != None						#=>True
re.search(r"^.$","*") != None						#=>True

#換行符的匹配
re.search(r"^.$","\n") != None						#=>False
#單行匹配
re.search(r"(?s)^.$","\n") != None					#=>True
re.search(r"^[\s\S]$","\n") != None					#=>True
複製代碼

貪婪與懶惰

當使用量詞匹配字符串有時會出現意料以外的錯誤狀況。

#字符串的值是"quoted string"
print(re.search(r"\".*\"","\"quoted string\"").group())
"quoted string"

#字符串的值是"string" and another"
print(re.search(r"\".*\"","\"quoted string\" and another\"").group())
"quoted string" and another" 複製代碼

咱們只想匹配"quoted string"可是下面的語句匹配到了錯誤的"quoted string" and another",這是由於默認的量詞匹配採用貪婪規則。就是在拿不許是否要匹配時,先嚐試匹配,而且記下這個狀態,以備未來"反悔"。這個「反悔」的過程叫作回溯(backtracking)

#準確匹配雙引號字符串,採用懶惰規則
print(re.search(r"\".*?\"","\"quoted string\" and another\"").group())
"quoted string"
複製代碼
貪婪匹配量詞 懶惰匹配量詞 限定次數
* *? 可能不出現,也可能出現,出現次數沒有上限
+ +? 至少出現1次,出現次數沒有上限
? ?? 至多出現1次,也可能不出現
{m,n} {m,n}? 出現次數最少爲m次,最多爲n次
{m,} {m,}? 出現次數最少爲m次,沒有上限
{,n} {,n}? 可能不出現,也可能出現,最多出現n次
jsStr = ''' <script type="text/javascript"> alert("1"); </script> <br /> <script type="text/javascript"> alert("2"); </script> '''


#貪婪匹配
jsRegex = r"<script type=\"text/javascript\">[\s\S]*</script>"
print(re.search(jsRegex,jsStr).group())

#輸出
''' <script type="text/javascript"> alert("1"); </script> <br /> <script type="text/javascript"> alert("2"); </script> '''

#懶惰匹配
jsRegex = r"<script type=\"text/javascript\">[\s\S]*?</script>"
print(re.search(jsRegex,jsStr).group())

#輸出
''' <script type="text/javascript"> alert("1"); </script> '''
複製代碼

轉義

各類量詞的轉義形式

量詞 轉義形式
{n} \{n}
{m,n} \{m,n}
{m,} \{m,}
{,n} \{,n}
* \*
+ \+
? \?
*? \*\?
+? \+\?
?? \?\?
. \.
#忽略轉義點號可能致使錯誤
#錯誤判斷浮點數
re.search(r"^\d+.\d+$","3.14") != None				#=>True
re.search(r"^\d+.\d+$","3a14") != None				#=>True

#準確判斷浮點數
re.search(r"^\d+\.\d+$","3.14") != None				#=>True
re.search(r"^\d+\.\d+$","3a14") != None				#=>False
複製代碼

括號

分組

使用括號()能夠將一個字符、字符組或表達式包圍起來做爲一個總體,再用量詞限定它們出現的次數,這種功能叫作分組

#用括號改變量詞的做用元素
re.search(r"^ab+$","ab") != None					#=>True
re.search(r"^ab+$","abb") != None					#=>True
re.search(r"^ab+$","abab") != None					#=>True
re.search(r"^(ab)+$","ab") != None					#=>True
re.search(r"^(ab)+$","abb") != None					#=>False
re.search(r"^(ab)+$","abab") != None                                    #=>True

#身份證號碼的準確匹配
r"^[1-9]\d{14}(\d{2}[0-9xX])?$"
複製代碼

多選結構

多選結構的形式是(...|…),在括號內以豎線|分隔開多個子表達式,這些表達式也叫多選分支(option);在一個多選結構內,多選分支的數目沒有限制。在匹配時,整個多選結構被視爲單個元素,只要其中某個子表達式可以匹配,整個多選結構的匹配就成功了;若是全部子表達式都不能匹配,則整個多選結構匹配失敗。

#用多選結構匹配身份證號碼
r"^([1-9\d{14}|[1-9]{16}[0-9xX]])$"

#準確匹配0-255之間的字符串
r"^([0-9]|[0-9]{2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"
複製代碼

多選結構的補充:

第1、多選結構通常會同時使用括號()和豎線|;可是沒有括號(),只出現豎線|,仍然是多選結構。

第2、多選結構並不等於字符組。字符組匹配要比多選結構效率高不少,字符組只能匹配單個字符,多選結構的每一個分支長度沒有限制。

第3、多選結構應當避免某段文字能夠被多個分支同時匹配的狀況,這將大大增長回溯的計算量,影響效率。若是遇到多個分支都能匹配的字符串,大多數語言優先匹配左側分支。

#多選結構的匹配順序
re.search(r"^jeffrey|jeff$","jeffrey").group()
'jeffrey'
re.search(r"^jeff|jeffrey$","jeffrey").group()
'jeff'
複製代碼

引用分組

使用括號以後,正則表達式會保存每一個分組真正匹配的文本,等到匹配完成後,經過**group(num)**之類的方法"引用"分組在匹配時捕獲的內容。其中num表示對應括號的編號,不管括號如何嵌套,分組編號都是根據開括號出現的順序來計數的;開括號是從左到右數起第多少個開括號,整個括號分組的編號就是多少。編號從1開始計數,不過也有0號分組,它是默認存在的,對應整個表達式匹配的文本。

#引用捕獲分組
re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group()
'2018-10-24'
re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group(0)
'2018-10-24'
re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group(1)
'2018'
re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group(2)
'10'
re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group(3)
'24'

#嵌套的括號
nestedGroupingRegex = r"(((\d{4})-(\d{2}))-(\d{2}))"
re.search(nestedGroupingRegex,"2018-10-24").group(0)
'2018-10-24'
re.search(nestedGroupingRegex,"2018-10-24").group(1)
'2018-10-24'
re.search(nestedGroupingRegex,"2018-10-24").group(2)
'2018-10'
re.search(nestedGroupingRegex,"2018-10-24").group(3)
'2018'
re.search(nestedGroupingRegex,"2018-10-24").group(4)
'10'
re.search(nestedGroupingRegex,"2018-10-24").group(5)
'24'
複製代碼

容易錯誤的狀況:

#容易弄錯的分組的結構
re.search(r"^(\d){4}-(\d{2})-(\d{2})$","2018-10-24").group(1)
'8'
複製代碼

這個表達式中編號爲1的括號是(\d),其中\d是「匹配一個數字字符「的子表達式,由於以後有量詞{4},因此整個括號做爲單個元素,要重複4次,並且編號都是1;因而每重複一次,就要更新一次匹配結果。因此在匹配過程當中,編號爲1的分組匹配文本的值,依次是2010,最後的結果是0。

#正則表達式的替換
re.sub(r"[a-z]"," ","1a2b3c")
'1 2 3 '
#在替換中使用分組
re.sub(r"(\d{4})-(\d{2})-(\d{2})",r"\2/\3/\1","2018-10-24")
'10/24/2018'
re.sub(r"(\d{4})-(\d{2})-(\d{2})",r"\1年\2月\3日","2018-10-24")
'2018年10月24日'
複製代碼

反向引用

**反向引用(back-reference)它容許在正則表達式內部引用以前的捕獲分組匹配的文本(也就是左側),其形式也是\num,其中num表示所引用分組的編號,編號規則與以前介紹的相同。

#用反向引用匹配重複字符
re.search(r"^([a-z])\1$","aa") != None                                          #=>True
re.search(r"^([a-z])\1$","ac") != None                                          #=>False

#用反向引用匹配成對的tag
pairedTagRegex = r"^<([^>]+)[\s\S]*?</\1>$"
re.search(pairedTagRegex,"<h1>title</h1>") != None                              #=>True
re.search(pairedTagRegex,"<h1>title</bold>") != None                            #=>False

#用反向引用匹配更復雜的成對tag
pairedTagRegex = r"^<([a-zA-Z0-9]+)(\s[^>]+)?>[\s\S]*?</\1>$"
re.search(pairedTagRegex,"<h1>title</h1>") != None                              #=>True
re.search(pairedTagRegex,"<span class=\"class1\">text</span>") != None		#=>True
re.search(pairedTagRegex,"<h1>title</bold>") != None                            #=>False
複製代碼

反向引用重複的是對應捕獲分組匹配的文本,而不是以前的表達式;也就是說,反向引用的是由以前表達式決定的具體文本,而不是符合某種規則的位置文本

#匹配IP地址的正則表達式
#匹配其中一段的表達式
segment = r"(0{0,2}[0-9]|0?[0-9]{2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
#正確的表達式
idAddressRegex = r"(" + segment + r"\.){3}" + segment
#錯誤的表達式
idAddressRegex = segment + r"\.\1.\1.\1"
複製代碼

各類引用的記法

語言 表達式中的反向引用 替換中的反向引用
.NET \num $num
Java \num $num
JavaScript $num $num
PHP \num \num或$num(PHP4.0.4以上版本)
Python \num \num
Ruby \num \num

通常來講,$num要好於\num。緣由在於,$0能夠準確表示「第0個分組」,而**\0則不行,由於很多語言的字符串中,\num自己是一個有意義的轉義序列,它表示值爲num的ASCII字符,因此\0會被解釋爲「ASCII編碼爲0的字符」。可是反向引用不存在這個問題,由於不能在正則表達式還沒匹配結束時,就用\0**引用整個表達式匹配的文本。

可是不管是**\num仍是$num**,都有可能遇到二義性的問題:若是出現了**\10**(或者**$10**),它究竟是表示第10個捕獲分組,仍是第1個捕獲分組以後跟着一個字符0?

Python將**\10**解釋成「第10個捕獲分組匹配的文本」,若是想表示第1個分組以後跟一個0,須要消除二義性。

#使用g<n>消除二義性
re.sub(r"(\d)",r"\g<1>0","123")
'102030'
複製代碼

Python和PHP的規定明確,因此避免了**\num的二義性;Java、Ruby、Javascript這樣規定\num**,若是一位數,則引用對應的捕獲分組;若是是兩位數且存在對應的捕獲分組時,引用對應的捕獲分組,若是不存在則引用一位數編號的捕獲分組。這樣若是存在編號爲10的捕獲分組,沒法用**\10**表示「編號爲1的捕獲分組和字符0」,若是在開發中遇到這個問題,現有規則下無解,但可使用明明分組解決此問題。

命名分組

爲了解決捕獲分組數字編號不夠直觀和會引發衝突的問題,一些語言提供了命名分組(named grouping)

在Python中用(?P<name>regex)來分組,其中的name是賦予這個分組的名字,regex則是分組內的正則表達式。

#命名分組捕獲
namedRegex = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
result = re.search(namedRegex, "2018-10-24")
print(result.group("year"))
2018
print(result.group("month"))
10
print(result.group("day"))
24

#命名分組捕獲時仍然保留了數字編號
print(result.group(1))
2018
print(result.group(2))
10
print(result.group(3))
24

#命名分組的引用方法
re.search(r"^(?P<char>[a-z])(?P=char)$","aa") != None			#=>True
re.sub("(?P<digit>\d)",r"\g<digit>0","123")
'102030'
複製代碼

不一樣語言中命名分組的記法

語言 分組記法 表達式中的引用記法 替換時的引用記法
.NET (?...) \k ${name}
Java7開始支持 (?...) \k ${name}
PHP (?P...) (?P=name) 不支持,只能使用\${num},其中num
爲對應分組的數字編號
Python (?P...) (?P=name) \g
Ruby (?...) \k \k

非捕獲分組

在使用分組時,只要出現了括號,正則表達式在匹配時就會把括號內的子表達式存儲起來,提供引用。若是不須要引用,保存這些信息無疑會影響正則表達式的性能;若是表達式比較複雜,要處理的文本又不少,更可能嚴重影響性能。

爲解決這種問題,提供了非捕獲分組(non-capturing group),非捕獲分組相似普通分組,只是在開括號後緊跟一個問號和冒號(?:...),這樣的括號叫作非捕獲型括號。在引用分組時,分組的編號一樣會按開括號的順序從左到右遞增,只不過必須以捕獲分組爲準,非捕獲分組會掠過

re.search(r"(\d{4})-(\d{2})-(\d{2})","2018-10-24").group(1)
'2018'
re.search(r"(?:\d{4})-(\d{2})-(\d{2})","2018-10-24").group(1)
'10'
複製代碼

轉義

括號的轉義必須轉義與括號有關的全部元字符包括()|。由於括號很是重要,因此不管時開括號仍是閉括號,只要出現,正則表達式就會嘗試尋找整個括號,若是隻轉義了開括號而沒有轉義閉括號,通常會報告"括號不匹配"的錯誤。另外,多選結構中的|也必須轉義。

#括號的轉義
re.search(r"^\(a\)$","(a)") != None                                     #=>True
re.search(r"^\(a)$","(a)") != None                                      #=>報錯

#未轉義|
re.search(r"^(\(a|b\))$","(a|b)") != None                               #=>False
re.search(r"^(\(a\|b\))$","(a|b)") != None                              #=>True
複製代碼

斷言

正則表達式中的大多數的結構匹配的文本會出如今最終的匹配結果中,可是有些結構並不真正匹配文本,而只負責判斷某個位置左/右側的文本是否符合要求,這種結構被稱爲斷言(assertion)。常見的斷言有三類:單詞邊界、行起始/結束位置、環視

待補充

我的根據《正則指引》內容總結記錄,侵刪!!