【读书】正则指引-3-括号
生活随笔
收集整理的這篇文章主要介紹了
【读书】正则指引-3-括号
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
分組
? ? ? 小括號(以下稱為括號)可以提供的第一個功能就是分組(grouping)。? ? ? 通過之前的學習,可以知道量詞用于限定之前元素的出現次數,這個元素可能是一個字符,也可能是一個字符組,還可能是一個表達式。如果把一個表達式用括號包圍起來,就形成了括號內的表達式,而這種表達式通常被稱為“子表達式”。
? ? ? 換句話說,子表達式就是利用括號分組功能形成的表達式。
? ? ??比如,(ab)+表示希望字符串ab重復出現一次以上。(對比ab+的含義)
? ? ? 分組的作用是可以準確表達“長度只能是m或 n”的語義。
分組功能使用場景:
- 身份證號碼的準確匹配 -- ^[1-9]\d{14}(\d{2}[0-9x])?$
- Open tag的(較)準確匹配 -- ^<[^/]([^>]*[^/])?>$
- Web 服務中的 URL rewrite 功能 -- 略
- E-mail地址匹配 -- ^[-\w]{0,64}@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$
多選結構
? ? ? 解決15和18位身份證匹配問題其實有兩種思路。第一種(上面給出的正則表達式),是將18位號碼多出的3位“合并”到匹配15位號碼的表達式中。第二種,則是直接將15位和18位身份證號分開處理。同樣還是使用括號,但使用的是括號的第二個功能,多選結構(alternative)。? ? ? 多選結構的形式是(…|…),即在括號內以豎線|分隔開多個子表達式,這些子表達式也叫做多選分支。在一個多選結構內,多選分支的數目沒有限制。在匹配時,整個多選結構被視為單個元素,只要其中某個子表達式能夠匹配,整個多選結構的匹配就成功。如果所有子表達式都不能匹配,則整個多選結構匹配失敗。
? ? ??所以,身份證號碼匹配的第二種方式為:([1-9]\d{14}|[1-9]\d{14}\d{2}[0-9x])
多選結構使用場景:
- 匹配IP地址的一段 -- ([0-9]|[0-9]{2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])
- 匹配手機號 -- (0|\+86)?(13[0-9]|15[0-356]|18[025-9])\d{8}
- 匹配月、日、小時、分鐘 -- 略
- tag的準確匹配 -- <(‘[^’]*’|”[^”]*”|[^’”>])+>
- 多選結構的一般表示法是(option1|option2),一般會同時使用括號()和豎線|,但是如果沒有括號,只出現豎線,仍然是正確的多選結構。在多選結構中,豎線用來分隔多選結構,而括號用來規定整個多選結構的范圍,如果沒有出現括號,則將整個表達式視為一個多選結構。所以ab|cd等價于(ab|cd)。推薦明確寫出兩端的括號,可以更加形象,也能避免一些隱晦的錯誤(因為豎線的優先級很低)。
- 多選分支并不等于字符組,雖然看起來類似字符組,如[abc]能匹配的字符串和(a|b|c)一樣。從理論上講,可以完全用多選結構來替換字符組,但這種做法不被推薦。理由:首先,[abc]比(a|b|c)簡潔,字符組有范圍表示法;其次,大多數情況下,[abc]比(a|b|c)的效率要高的多。反過來,多選結構不一定能對應到字符組。因為字符組的每個“分支”的長度相同,而且只能是單個字符;而多選結構的每個“分支”的長度沒有限制,甚至可以是復雜的表達式,比如(abc|b+c*ab),字符組對這種匹配完全無能為力。
- 字符組有排除型字符組,但多選結構卻沒有對應的結構來表達該語義。
- 多選分支的排列是有講究的。例如,表達式(jeff|jeffrey),用它匹配jeffrey,結果是jeff還是jeffrey呢?這個問題其實沒有標準答案,大多數語言的多選結構都會按照優先選擇最左側的分支來進行匹配。
- 在平時使用中,如果出現多選結構,應當盡量避免多選分支中存在重復匹配,因為這樣會大大增加回溯的計算量。
引用分組
? ? ? 括號不僅僅能把有聯系的元素歸攏起來并分組,還有其他作用,即使用括號之后,正則表達式會保存每個分組真正匹配的文本,等到匹配完成后,可以通過group(num)之類的方法“引用”分組在匹配時捕獲的內容。其中num表示對應的編號,括號分組的編號規則是從左向右計數,從1開始。這就是括號的第三個功能,捕獲分組(capturing group)。這種括號叫做捕獲型括號。? ? ? 一般來說,正則表達式匹配完成之后,都會得到一個表示“匹配結果”的對象,對它調用獲取分組的方法,傳入分組編號num,就可以得到對應分組匹配的文本。num的編號是從1開始的,不過編號為0的分組也是默認存在的,其對應整個表達式匹配的文本。
? ? ? 關于括號存在嵌套的問題:無論括號如何嵌套,分組的編號都是根據開括號出現順序來計數的,開括號是從左向右起第多少個開括號,整個括號分組的編號就是多少。
引用分組是使用場景:
- 提取HTML中的所有超鏈接的地址和文本 -- <a\s+href\s*=\s*[“’]?([^”’\s]+)[“’]?>([^<]+)</a>
- 提取HTML中的標題相關內容 -- <head>([^<]+)</head>
- 提取HTML中的圖片鏈接相關內容 -- <img\s+[^>]*?src=[‘”]?([^”’\s]+)[‘”]?[^>]*>
在提取日期 2010-12-22 這個內容時,正確的正則表達式為
(\d{4})-(\d{2})-(\d{2})
而錯誤的表達式可能被寫成
(\d){4}-(\d{2})-(\d{2})
引用分組捕獲的文本,不僅僅用于數據提取,也可以用于替換。
? ? ? 在各種語言中都有類似substitute(pattern,replacement,string)這種形式的正則替換命令。其中replacement處可以使用引用分組,形式是\num(有些語言中為$num,下文同,本文中只寫作\num),其中num為對應分組的編號。同時要明白replacement處為普通字符串,在某些語言中,需要特別指明replacement使用原生字符串才行,否則\num這種引用分組在普通字符串中會被當做不合法的轉義序列處理。
? ? ? 另外,如果想在replacement中引用整個表達匹配的文本,不能使用\0,即便使用原生字符串也不行。一種變通策略是給整個表達式加上一對括號,之后使用\1來引用。
反向引用
? ? ? 反向引用(back-reference)主要用于檢測重疊出現的內容,即允許在正則表達式內部引用之前的捕獲分組匹配的文本。其使用形式也是\num。反向引用的使用場景:
- 檢查某個單詞是否包含重疊出現的字母 -- ^([a-z])\1$
- 解析HTML代碼時匹配簡單tag -- <([^>]+)>[\s\S]*?</\1>
- 解析HTML代碼時匹配復雜tag -- <([a-zA-Z0-9]+)(\s[^>]+)?>[\s\S]*?</\1>
- 處理中文文本 -- 略
各種引用的記法
? ? ? 上面說過,對分組進行引用的時候使用\num這類表示法,同時還說過,還有另外一些語言使用了不同的表示方式。如下圖所示? ? ? 一般情況下,我們認為$num要好于\num。原因在于,$0可以準確表示“第0個分組(也就是整個表達式匹配的文本)”,而\0則不行,因為不少語言的字符串中,\num本身可能就是一個有意義的轉義序列,表示值為num的ASCII碼字符,所以\0會被解釋成“ASCII編碼為0的字符”。
? ? ? 二義性問題:無論采用\num還是$num進行引用都會遇到二義性問題,如果出現了\10,它到底表示第10個捕獲分組\10,還是第1個捕獲分組\1之后跟著一個字符0?
? ? ? 各種語言的解決方式有所不同,Python和PHP中提供了特定的表示法來專門解決這類問題(細節略)。而Java、Ruby和JavaScript中則是通過制定規則來解決這類問題的:如果是一位數,則引用對應的捕獲分組;如果是兩位數且存在對應捕獲分組時,引用對應的捕獲分組,如果不存在對應的捕獲分組,則引用一位數編號的捕獲分組。
? ? ??簡單思考一下就可以發現,后面這幾種語言的策略存在一個問題無法解決:如果存在編號為10的捕獲分組,無法用\10表示“編號為1的捕獲分組和字符0”,因為此時\10表示的必然是編號為10的捕獲分組。在實際開發中,尤其是進行文本替換時有時確實會遇到這個問題,在現有的規則下是無解的。所幸的是,一般情況下,我們根本不會用到太多的捕獲分組(即基本上用不到\10以上的分組);另外,已經有越來越多的語言提供了命名分組,它可以徹底解決這個問題。
命名分組
? ? ? 使用數字編號來標識捕獲分組存在不夠直觀,括號多了容易搞錯序號,引用時不夠方便的問題。所以,一些語言和工具提供了命名分組(named grouping),可以將它看成另一種捕獲分組。? ? ? 命名分組在每種語言中各有不同,此處不做展開。另外,由于歷史原因和后向兼容性,即便使用了命名分組,每個命名分組同時也具有數字編號,其編號規則沒有變化。
非捕獲分組
? ? ? 前面介紹了括號的三種主要用途,需要指出的是,各種用途間不是彼此獨立的,而是互相重疊的:單純的分組可以視為“只包含一個多選分支的多選結構”;整個多選結構也會被視為單個元素,可以由單個量詞限定。最重要的是,無論是否需要引用分組,只要出現了括號,正則表達式在匹配時就會把括號內的子表達式存儲起來,提供引用。如果并不需要引用,保存這些信息無疑會影響正則表達式的性能;如果表達式比較復雜,要處理的文本又很多,更可能嚴重影響性能。? ? ? 為解決這種問題,正則表達式提供了非捕獲分組(non-capturing group),非捕獲分組類似普通的捕獲分組,只是在開括號后緊跟一個問號和冒號(?:…),這樣的括號叫做非捕獲型括號。它的作用只是限定量詞的作用范圍,不捕獲任何文本。在引用分組時,分組的編號同樣會按照開括號出現的順序從左向右遞增,只是必須以捕獲分組為準,非捕獲分組會略過。
補充
括號的轉義
? ? ? 括號的轉義要求和括號相關的所有三個元字符(、)、|都必須轉義\(、\)、\|。URL Rewrite
? ? ? URL Rewrite是常見web服務器都具備的功能,用來進行網址的轉發。例子如下外部訪問 URL
http://www.example.com/blog/2006/12
內部實現
http://www.example.com/blog/posts.php?year=2006&month=12
? ? ? 這樣重寫的好處是隔離了外部接口和內部實現,方便修改;也有利于提供更有意義、更直觀的URL。
? ? ? 一般來說,URL Rewrite都是使用轉發規則實現的,每條轉發規則對應一類URL,以正則表達式解析并提取出所有需要的信息,重組之后再轉發。
? ? ? 學習URL Rewrite的使用可以參考當前的各類主流Web服務器的配置,此處略。
總結
? ? ? 本篇將《正則指引》的第三章內容進行了概括總結,下次講解正則表達式中的斷言。參考資料
? ? ? 《正則指引》:余晟。?轉載于:https://my.oschina.net/moooofly/blog/377757
總結
以上是生活随笔為你收集整理的【读书】正则指引-3-括号的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot项目根据不同的环境启
- 下一篇: LINQPad工具-linq、sql、I