爲了方便閱讀,省略英文.
另外一個重複匹配字符是+,可以匹配一個或者是多個字符。請注意+和*的區別,*是匹配零個和多個,+是匹配一個或者是多個。來看個例子吧,ca+t可以匹配cat(1個a),caaat(3個a),但是不能匹配ct,因爲ct中缺少a.
還有兩個重複匹配的限定符,一個是問號,?,可以匹配一次或者零次;?一般用來表示某些字符時可選的。比如在英語中分行符-, home-?brew 可以匹配沒有分行符的單詞 homebrew 或者具有分行符的單詞 home-brew.
另外一個重複匹配限定符就是{m,n}, 這是是一個最靈活的限定符,上面講的一切的限定符都可以使用這個通用的重複限定符來表達。在這個表達式中, m 和n都是十進制。這個表達式意思是,重複至少爲m次,至多爲n次。舉個例子來說, a/{1,3}b 可以匹配 a/b, a//b, 以及 a///b. 但是不會匹配 ab, 原因是其沒有包含/字符, 他也不能匹配 a////b, 這個是因爲含有四個/。
在這個強大的表達式中,我們可以生類m和n。這樣的話,我們會使用缺省值來代替我們省略的m或者。假使我們省略m的話我們會使用0來代替。如果省略了n,我們會使用語言的int類型的上限,也就是2^31,這個值近似於無限。
喜歡偷懶的人也許注意到了,其實*+?都可以使用{m,n}來表示。我們可以將其看成是{m,n}的偷懶的方式。{0,} 跟 *是一樣的。{1,} 跟 +是一樣的, {0,1} 跟 ?是一樣的。鼓勵大家使用偷懶的方式 *, +, 或者 ?,原因是這些字符易讀,還有一個原因是引擎對這些字符做了優化。
使用正規則表達式
現在我們來看一些簡單的正則表達式。我們首先想到的是我們如何使用正則表達式?不用擔心,re模塊已經幫我們實現了一個python 的正則表達式引擎。Re模塊爲了速度是使用C語言編寫的。所以使用正則表達式比使用普通的字符串操作可以得到更好的效率。爲了更加進一步的提高效率,我們可以將模式串編譯成二進制結構,然後再使用正則匹配。當你要匹配很多次的時候,這個還是挺必要的。
編譯模式串
被編譯的模式串可以用來匹配或者用來進行模式替換。
>>>
>>> importre
>>> p = re.compile('ab*')
>>> p
<_sre.SRE_Pattern object at 0x...>
re.compile()也可以接受 flags 參數, 用來使能各種特殊的功能或者是使能各種語法變體。我們在稍後的文章中會一一介紹。現在我們先來看簡單的例子。
>>> p = re.compile('ab*', re.IGNORECASE)
傳入re.compile()的RE參數是一個標準的字符串. 正則表達式的模式表達式不是python 的核心部分,他們也沒有特殊的語法,所以RE只能以標準字符串的形式傳入. (有些應用根本就不會使用正則表達式,所以我們也沒有必要將其納入語言的核心) 爲了將RE引入到python中,我們採用了跟socket 和 zlib一樣的策略,將其作爲模塊引入。
將模式使用字符串表示保持了python簡潔的一貫風格,但是也帶來了很多的負面影響。我們會在下一個章節中稍作介紹。
都是反斜槓惹的禍
前面已經提到過,正則表達式使用('\')來制定一些特殊的形式或者讓一些特殊的匹配字符恢復他們的本來面貌。這個可能會個python中有些字符串的真實字面值有衝突。(挺繞口的,看例子吧)
我們會在LaTeX中經常使用\section,作爲起始,來表示我們在編寫的代碼中寫了什麼東西。假使你要寫一個模式來匹配 \section, 你必須要使用反斜槓來解除反斜槓或者其他特殊字符的特殊功能。所以我們會把模式寫成 \\section. 我們最終要傳給re.compile()的一定是 \\section. 不要忘記,python同樣使用反斜槓,所以我們必須再次使用反斜槓來解除我們傳遞給re.compile()的兩個反斜槓。看具體的實現吧。
Characters |
Stage |
\section |
我們匹配的字串 |
\\section |
re.compile()要使用的串 |
"\\\\section" |
傳遞給re.compile()的串 |
簡而言之,爲了匹配一個反斜槓我們必須使用四個反斜槓'\\\\' ,真正的串中需要兩個,爲了構造這兩個反斜槓,我們必須繼續使用反斜槓。所以這幾造成了反斜槓風暴. 在正則表達式中重複的使用反斜槓,會造成反斜槓風暴,導致串極難讀懂。(我來試着解釋一下原因,沒有經過驗證,純屬猜測:RE模塊本身使用C語言編寫,C語言中同樣會使用兩個反斜槓來表示一個反斜槓,這個挺容易理解的。我們難於理解的是爲什麼還在需要兩個呢?原因在於python的內置的字符串類型給C的char*不能通用,這裏面存在一次轉換,在python將串傳遞給C的我們必須構造一個C的字符串。)
一種解決方案是是使用python 的內置的原生串來表示正則表達式。以'r'開頭的字符串反斜槓不會做任何處理,比如 r"\n" 是兩個字符 '\' 和 'n', 而 "\n" 表示一個換行符.在python中正則表達式我們強烈建議使用原生串書寫.
Regular String |
Raw string |
"ab*" |
r"ab*" |
"\\\\section" |
r"\\section" |
"\\w+\\s+\\1" |
r"\w+\s+\1" |