RE:用匹配來演繹編程的藝術
學習一時爽,一直學習一直爽
Hello,大家好,我是 Connor,一個從無到有的技術小白。上一次我們說到了 pyquery
今天我們將迎來我們數據匹配部分的最後一位重量級人物,也是編程語言中普及率最高的一個東西,它就是正則。正則長期以來佔據着編程新手的禁忌之地,大家對它是又愛又恨。今天,我們將揭開他神祕的面紗,直面正則,並助你征服它,讓它成爲你的得力助手!
1. 正則的介紹
由於正則並不是 Python
所獨有的內容,本文大部分會以正則的角度來進行描述和講解,而不侷限於 re 庫,文章會穿插以 re 爲例,對正則的例子進行講解。
1.1 正則的起源
想要徹底掌握正則,就要從頭到尾的瞭解它。那麼正則是如何誕生的呢?這要追溯到上世紀人們對人類神經系統如何工作的早期研究。兩位神經生理學家研究出一種數學方式來描述這些神經網絡。具體叫什麼我就不說了。
1956 年, 一位數學家在上面兩位神經生理學家早期工作的基礎上,發表了一篇標題爲“神經網事件的表示法”的論文,引入了正則表達式的概念。正則表達式就是用來描述他稱爲“正則集的代數”的表達式,因此採用“正則表達式”這個術語。
隨後他們發現可以將這一研究成果廣泛的應用於其它領域的研究。Ken Thompson 使用它來進行一些早期的計算搜索算法的一些研究,Ken Thompson 是 Unix 的主要發明人。正則表達式的第一個實用應用程序就是 Unix 中的 qed 編輯器。
然後,正則表達式在各種計算機語言或各種應用領域得到了廣大的應用和發展,演變成爲目前計算機技術森林中的一隻形神美麗且聲音動聽的百靈鳥。到目前正則表達式在基於文本的編輯器和搜索工具中依然佔據這一個非常重要的地位。
在最近的六十年中,正則表達式逐漸從模糊而深奧的數學概念,發展成爲在計算機各類工具和軟件包應用中的主要功能。不僅僅衆多UNIX工具支持正則表達式,近二十年來,在 Windows 的陣營下,正則表達式的思想和應用在大部分 Windows 開發者工具包中得到支持和嵌入應用!
Windows 系列產品對正則表達式的支持發展到無與倫比的高度,目前幾乎所有 Microsoft 開發者和所有.NET語言都可以使用正則表達式。包括 Python、Java、JavaScript、Go 等非 Windows 系列的產品也對正則進行了支持,這無疑將正則變成了編程界的寵兒,可以說得正則者得天下!
所以,看到這裏是不是想要迫不及待地來學會正則了???讓我們一起來看看正則的概念:
1.2 正則的概念
正則表達式是對字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個“規則字符串”,這個“規則字符串”用來表達對字符串的一種過濾邏輯。
Emmn… 看了這麼官方的描述還是有些懵逼,說白了正則表達式就是通過一些指定好的特殊字符來代替某種字符。舉個例子:大家小時候都玩過撲克遊戲排火車,就是當你現在排的牌和前面的任意一張大小相同的時候,無論中間有多少,有什麼牌你都可以獲取這中間所有的牌。正則和這個類似,無視中間所有的東西,只要滿足你這個規則,就可以取出中間所有的內容!
不知道我的例子夠不夠清晰,反正我絞盡腦汁也只想到了這一個比較像的例子,如果你有什麼更好的例子,可以留言或評論,我將引用你的例子。
說完了正則的概念,我們再來看看正則有哪些特點吧!
1.3 正則的特點
正則表達式有如下的特點:
- 靈活性、邏輯性和功能性非常的強;
- 可以迅速地用極簡單的方式達到字符串的複雜控制。
- 適用於任何字符串
2. 正則表達式的基礎知識
這裏往後都是基礎的東西,如果你覺得你都懂了想直接看Python的re庫請 點擊這裏>>
既然大概瞭解了正則的歷史以及他的功能和特點,那麼讓我們從正則表達式的基礎知識來開始學習吧。大多數教程都是從元字符開始的,但是他們忽略了一些最基礎的東西,這些東西對於初級運用或許沒有任何關係,但是當你要使用到高級用法的時候很可能會造成混亂,甚至是遺忘,所以我們在開始前先來說一說正則的基礎知識:
2.1 字符串的位置講解
在我們的印象中,光標的位置就是在字符之後的,但是實際上在底層,光標指的並不是當前字符的位置,而是光標前面的這個位置,如圖:
真實的光標位置指的是光標之前的這個字母,是這個字母 e 的位置,所以在一個長度爲 n 的字符串中,總共有 n + 1 個位置。例如:
(0)a(1)b(2)c(3)
在這個長度爲 3 的字符串 abc 中,一共有 (0)(1)(2)(3)四個位置
2.2 佔有字符和零寬度
可能現在說佔有字符和零寬度會讓你感覺有些不知所措,但是當你看完後面的內之後再反過來看佔有字符和零寬度的時候你就會恍然大悟,將這兩個概念瞭然於心了。
正則表達式匹配過程中,如果子表達式匹配到東西,而並非是一個位置,並最終保存到匹配的結果當中。這樣的就稱爲佔有字符,而只匹配一個位置,或者是匹配的內容並不保存到匹配結果中,這種就稱作零寬度,佔有字符是互斥的,零寬度是非互斥的。也就是一個字符,同一時間只能由一個子表達式匹配,而一個位置,卻可以同時由多個零寬度的子表達式匹配。
2.3 正則表達式的控制權與傳動
正則表達式由左到右依次進行匹配,通常情況下是由一個表達式取得控制權,從字符串的的某個位置進行匹配,一個子表達式開始嘗試匹配的位置,是從前一個匹配成功的結束位置開始的。
3. 正則表達式的語法
3.1 正則表達式的元字符
正則表達式的元字符是正則表達式的根本,所有的正則表達式都有元字符組成,正則表達式的元字符包括下列幾種:
3.1.1 基本元字符
字符 | 描述 | |
---|---|---|
. | 匹配除換行符外的任意字符 | |
* | 匹配0個或多個符合表達式的字符 | |
? | 匹配0個或1個符合表達式的字符 | |
+ | 匹配1個或多個符合表達式的字符 | |
[…] | 匹配方括號內的任意字符 | |
[^…] | 匹配除方括號字符內的任意字符 |
3.1.2 轉義元字符
字符 | 描述 |
---|---|
\ | 用於轉義下一個字符 |
\A | 僅匹配字符串的開頭 |
\b | 匹配一個單詞的邊界 |
\B | 匹配非單詞邊界 |
\cX | 匹配指明的控制字符 |
\d | 匹配一個數字,等價於[0-9] |
\D | 匹配一個非數字,等價於[^0-9] |
\f | 匹配一個換頁符,等價於\x0c和\cL |
\n | 匹配一個換行符,等價於\x0a和\cJ |
\t | 匹配一個製表符,等價於\x09和\cI |
\r | 匹配一個回車符,等價於\x0d和\cM |
\s | 匹配 一個不可見的字符 |
\S | 匹配一個可見的字符 |
\v | 匹配一個垂直製表符,等價於\x0b和\cK |
\w | 匹配包括下劃線的任何單詞字符 |
\W | 匹配任何非單詞字符 |
\Z | 僅匹配字符串的末尾 |
\xn | 匹配轉移爲\xn的十六進制轉義字符 |
\un | 匹配一個Unicode轉義字符 |
3.1.3 限定類元字符
字符 | 描述 |
---|---|
{n} | 匹配確定的n個字符 |
{n,} | 匹配至少n個字符 |
{n,m} | 匹配至少n個字符,至多m個字符 |
| | 或符號,用於連接多個表達式 |
- | 範圍符,只能用於方括號中,如[a-z] |
^ | 匹配字符串的開頭,在方括號中表示取反 |
$ | 匹配字符串的末尾 |
(parameter) | 將括號內的內容進行匹配並分組 |
(?!parameter) | 正向否定預查,匹配不是parameter的內容 |
(?=parameter) | 正向肯定預查,匹配是parameter的內容 |
(?:parameter) | 匹配parameter獲取匹配結果 |
(?<=parameter) | 反向肯定預查,在任何匹配的paraemter處開始匹配字符串 |
(?<!parameter) | 反向否定預查,在任何不符合parameter的地方開始匹配字符串 |
只是在這裏向你羅列元字符你是不會懂如何使用正則的,請稍安勿躁,稍後我們將以 Python 中的正則爲例,爲大家逐一展示各種元字符的用法。如果你比較急切,請點擊這裏來快速查看 re 庫的使用方法。瞭解了元字符,我們再來認識一下可打印字符與非打印字符:
3.2 打印字符與非打印字符
3.2.1 打印字符
如你所見,你在網上瀏覽到的所有的字符,這包括所有大寫和小寫字母、所有數字、所有標點符號和一些其他符號。這些全部可見的字符都叫做可打印字符
3.2.2 非打印字符
非打印字符也可以是正則表達式的組成部分。下表列出了表示非打印字符的轉義序列:
字符 | 描述 |
---|---|
\cx | 匹配由x指明的控制字符。 |
\f | 匹配一個換頁符。等價於 \x0c 和 \cL。 |
\n | 匹配一個換行符。等價於 \x0a 和 \cJ。 |
\r | 匹配一個回車符。等價於 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、製表符、換頁符等等。等價於 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等價於 [^\f\n\r\t\v ] 。 |
\t | 匹配一個製表符。等價於 \x09 和 \cI。 |
\v | 匹配一個垂直製表符。等價於 \x0b 和 \cK。 |
打印字符和非打印字符非常易懂,沒什麼難點,一筆帶過,下面我們來看看運算符的優先級:
3.3 正則表達式運算符的優先級
正則表達式從左到右進行計算,並遵循優先級順序,這與算術表達式非常類似。相同優先級的從左到右進行運算,不同優先級的運算先高後低。下表從最高到最低說明了各種正則表達式運算符的優先級順序:
運算符 | 描述 |
---|---|
\ | 轉義符 |
(), (?: ), (?=), [] | 圓括號和方括號 |
*, +, ?, {n}, {n,}, {n,m} | 限定符 |
^, $, \任何元字符、任何字符 | 定位點和序列(即:位置和順序) |
| | 替換,"或"操作字符 |
運算優先級非常類似於四則運算,沒什麼好說的,下面我們來看看正則的匹配模式:
3.4 正則的匹配模式(表達式修飾符)
正則表達式有時候不只是需要使用簡單的正則語句,有時候還需要挑選合適的匹配模式(表達式修飾符),通過這些不同的匹配模式,可以提升匹配的效率。正則的匹配模式如表:
模式 | 描述 |
---|---|
/a | 強制從字符串頭開頭進行匹配 |
/i | 不區分大小寫匹配 |
/g | 全局匹配 |
/m | 多行匹配 |
/s | 特殊字符匹配 |
/u | 不重複匹配 |
/x | 忽略模式中的空白 |
到這裏就介紹完元字符了,如果你想直接看元字符的使用示例,請 點擊這裏>>
4. Python 的正則庫 – re庫
正則被廣大編程語言所支持,Python也不例外,Python 官方爲我們封裝了 re 庫來支持正則的使用,下面我們一起來走進 Python 的 正則庫–re庫:
4.1 Python 中的正則匹配模式
Python 的正則也是正則,所以它也有匹配模式。但這匹配模式又有些不同於正則的表達方式,我們來看一看:
修飾符 | 描述 |
---|---|
re.A | 強制從字符串頭開始匹配 |
re.I | 使匹配對大小寫不敏感 |
re.L | 做本地化識別(locale-aware)匹配 |
re.M | 多行匹配,影響 ^ 和 $ |
re.S | 使 . 匹配包括換行在內的所有字符 |
re.U | 根據Unicode字符集解析字符 |
re.X | 該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解。 |
可以看到,匹配模式並不再是 /*
來進行表示了,而是變成了 re.*
來進行表示了。
4.2 Python 中的正則常用方法
Python 既然是一種語言,那麼它定然不會讓我們傻敷敷的去使用,Python 爲我們封裝好了方法,我們來逐一看一下都有哪些方法吧:
4.2.1 compile()
re.compile(pattern, flags=0)
這個方法用來將正則表達式進行編譯,返回一個Pattern對象。這麼說你一定還是很懵逼,我們來舉個例子:
import re
string = "Learning makes a good man better and ill man worse."
pattern = re.compile(r"m\w+")
result = pattern.findall(string)
print(result)
返回結果:
['makes', 'man', 'man']
這樣就顯而易見了,先將正則表達式進行編譯,在下次使用的時候直接給編譯好的正則表達式一個字符串讓它去匹配就好了。這樣做的好處是提高了性能,如果你要處理大文檔,建議你這樣使用。
4.2.2 escape()
escape(pattern)
這個方法可以用來匹配除 ASCII字符,數字和特殊字符 ‘_’ 以外的所有字符。官方的說法就是不一樣,這個東西有時候真的挺讓人懵逼的,簡單來說,這個方法就是用來消除字符串中的任何特殊字符效果的。
我舉個例子你就明白了。有時候問哦們在匹配網址的時候,因爲 ‘.’ 是特殊字符,所以有些時候我們無法成功的進行匹配,我們需要轉義後再進行匹配,大部分人的操作是這樣的:
import re
string = "www.baidu.com"
result = re.findall(r"baidu\.com", string)
print(result)
這樣做確實可以達到我們想要的效果,但是對於想要玩騷操作的我們來說,這樣太 Low 了,所以就有了這樣的方式:
import re
string = "www.biadu.com"
result = re.findall(re.escape("baidu.com"), string)
print(result)
這樣一來,我們就省去了寫轉義的麻煩步驟,案例中需要轉移的內容較少,可能看不出有什麼優勢,但是當你匹配大量需要轉移的內容的時候,這樣做不僅可以省去打轉義字符的麻煩,而且也防止我們漏打,錯打轉義字符。
當然,你也要注意一點,使用這個方法會消除一切特殊字符的特殊效果,使用這種方法匹配具有一定的侷限性,不能再進行模糊匹配了,請按需使用。
4.2.3 findall()
findall(pattern,string,flags = 0)
通過這個方法匹配出來的是字符串中所有非重疊匹配的列表。老規矩,舉個例子:
import re
string = "Learning makes a good man better and ill man worse."
result = re.findall(r'm\w+', string)
print(result)
運行結果:
['makes', 'man', 'man']
4.2.4 finditer()
finditer(pattern, string, flags=0)
通過這個方法可以將字符串中所有非重疊匹配上返回迭代器。對於每個匹配,迭代器返回一個匹配對象,匹配結果中包含有空對象。
簡單的說,就是返回一個可迭代的 Objct
對象,這是它和 findall()
方法唯一的區別。 我們舉個例子:
import re
string = """ QQ郵箱: [email protected]
網易郵箱: [email protected]
126郵箱: [email protected]"""
ls = re.finditer(r"(\d+@\w+.com)", string, re.S)
print(type(ls))
for item in ls:
print(item.group())
運行結果如下:
<class 'callable_iterator'>
123456789@qq.com
123456789@163.com
123456789@126.com
生成的可迭代的 Object
對象中,內部對象都爲 SRE_Match
對象,該對象不可直接提取匹配的內容,需要通過 group()
方法來進行提取。
4.2.5 fullmatch()
fullmatch(pattern, string, flags=0)
通過這個方法可以用來判斷整個字符串是否匹配規則,如果符合規則則返回 match object。不符合則返回 None。舉個例子:
import re
string = """QQ郵箱: [email protected]"""
ls = re.fullmatch(r"QQ郵箱: [email protected]", string, re.S)
print(ls.group())
運行結果如下:
QQ郵箱: 123456789@qq.com
如果我們隨意改變一下規則:
import re
string = """QQ郵箱: [email protected]"""
ls = re.fullmatch(r"QQ郵箱", string, re.S)
print(ls)
運行結果:
None
可以看得出,只有當整個字符串都符合匹配規則的時候才能匹配到東西,否則的話返回的是 None
。
4.2.6 match()
match(pattern, string, flags=0)
使用這個方法可以嘗試在字符串的開頭進行匹配,返回匹配對象,如果未找到匹配,則返回None。
你應該注意到了,它是在字符串的開頭進行匹配的,如果你還是不太清楚,那我們接着來看例子:
import re
string = """QQ郵箱:[email protected]"""
ls = re.match(r"QQ郵箱", string, re.S)
print(ls)
運行結果如下:
<_sre.SRE_Match object; span=(0, 4), match='QQ郵箱'>
因爲我們匹配的法則在開頭有,如果我們換個規則的話:
import re
string = """QQ郵箱:[email protected]"""
ls = re.match(r"\d+", string, re.S)
print(ls)
運行結果:
None
按照我們的想法它應該會匹配到@前面的一串數字吧?但是實際上它並沒有匹配到。這就是所謂的在字符串的開頭進行匹配。
4.2.7 purge()
通過這個方法可以清除正則表達式的緩存。這個方法貌似沒有啥好說的 …
4.2.8 search()
search(pattern, string, flags=0)
搜索整個字符串以匹配符合規則的字符串,如果有則返回匹配對象,如果沒有則返回None。前面說的 match()
方法是隻在頭部進行匹配,而 search()
方法可以在字符串中的任意位置進行匹配。例如:
import re
string = """QQ郵箱:[email protected]"""
ls = re.search(r"\d+", string, re.S)
print(ls)
運行結果如下:
<_sre.SRE_Match object; span=(5, 14), match='123456789'>
可以看的出,沒有在字符串首部的字符也被匹配出來了。相比 match()
方法,search()
方法,findall()
方法要用的更多些。
4.2.9 split()
split(pattern,string,maxsplit = 0,flags = 0)
看到這個方法我想大家第一時間想到的是字符串中的 split()
方法,恭喜你,猜對了,這個方法和 字符串中自帶的 split()
方法大同小異,這個方法增加的功能是可以通過正則表達式來進行分割。例如:
首先舉一個都能理解的功能:
import re
string = """abc,123456789,@qq.com"""
ls = re.split(r",", string)
print(ls)
運行結果:
['abc', '123456789', '@qq.com']
可以看得出,現在的分割方法和 字符串中自帶的 split()
方法沒有什麼區別。下面我們再看多出來的用法:
import re
string = """[email protected]"""
ls = re.split(r"\d+", string)
print(ls)
運行結果:
['abc', '@qq.com']
可以看得出,該方法將 數字作爲分割依據進行了分割。如果你足夠細心的話,你會發現這個方法還有一個參數,叫 maxsplit
這個方法代表着最大分割次數。比如:
import re
string = """abc,123456789,@qq.com"""
ls = re.split(r",", string, 1)
print(ls)
運行結果如下:
['abc', '123456789,@qq.com']
可以看到它只進行了一次分割,maxsplit
就是用來控制分割次數的,分割從左到右依次進行分割。分割 maxsplit 次。
4.2.10 sub()
sub(pattern,repl,string,count = 0,flags = 0)
將所有符合規則的字符串替換成 repl。最多替換 count個,默認全部替換。這是我個人認爲正則中除了 findall()
方法外最有用的方法了。批量替換超級有用!!!
咳咳,激動了啊,下面我們來說說這個方法的使用:
import re
string = """[email protected]"""
ls = re.sub(r"456", "abc", string)
print(ls)
運行結果如下:
abc123abc789@qq.com
一下就把字符串中的 456 給替換成 abc了,賊帶勁。用了這個方法你還想用 replace方法嗎?那個方法多傻,如果你不覺得,一會我會讓你認同我的觀點的。我們先往下說,sub()
方法也有一個計數參數 count,我們可以通過給它賦值來指定替換個數。比如:
import re
string = """[email protected]"""
ls = re.sub(r"abc", "---", string, 2)
print(ls)
運行結果:
---12---67abc@qq.com
在上面的程序中我們指定了替換個數2,所以結果中只替換了兩個 abc,並沒有全部替換。如果不指定 count 參數的話,默認會進行全部替換。
4.2.11 subn()
subn(pattern,repl,string,count = 0,flags = 0)
這個方法和 sub()
方法沒有什麼本質上的區別,唯一的不同就是它返回的是一個元組,這個元組包含有兩個內容,(1)替換後的字符串,(2)整個字符串中的替換次數。舉個例子:
import re
string = """[email protected]"""
ls = re.subn(r"abc", "---", string)
print(ls)
運行結果如下:
('[email protected]', 3)
4.2.12 template()
template(pattern, flags=0)
這個方法我查了很多資料,國內的網站並沒有幾個人寫這個方法的,可能有寫的,但是沒有用這個標題來進行發表吧,總之我沒有找到什麼詳細的資料,但是通過他的官方解釋:“編譯模板模式,返回模式對象” 可以看得出它返回的是一個模式對象,和 re.compile()
方法類似,返回的都是一個模式對象。應該用法和 re.complie()
相似。
但是我查閱了國外的網站,大家有提到說發現這個函數,但是並沒有發現有什麼用處。大家說,可能這個函數並未實現預期想要的功能,或者這個函數出現了什麼問題而關閉了。所以只能暫時停留在這個狀態。當然平時我們也用不到這個函數
我通過自己的琢磨,自己的嘗試,發現這個函數大部分函數功能是和 compile()
方法相同的。比如:
import re
abc = re.template(r"abc", re.I)
string = "abcasdfadsfABCasdfabcasdfasdf"
result = abc.findall(string)
print(result)
運行結果如下:
['abc', 'ABC', 'abc']
但是最主要的問題是,該方法禁用所有的特殊字符,即re的元字符,比如:
import re
abc = re.template(r"\d+", re.I)
string = "abcasdfadsfABCasdfabcasdfasdf"
result = abc.findall(string)
print(result)
運行結果如下:
Traceback (most recent call last):
File "G:/Python/PyWin/test.py", line 2, in <module>
abc = re.template(r"\d+", re.I)
....
這個情況下就直接報錯了。所以我想不出來這個函數有什麼作用。之所以寫出來就當給大家開眼了。如果真的需要使用模板的話,還是建議大家使用 re.compile()
方法來進行操作。
好了,這就是 Python 正則庫 re 中的所有函數了。下面我還有幾個內容要給大家說
4.3 re 中的 Pattern 對象
這個Pattern 我爲什麼不在講方法之前說呢,就是怕你們不明白,讓我一下子給弄暈了,所以我選擇在講完所有方法之後再說。這樣的話有些人不需要了解這麼深,直接找到他們想要的方法,直接去使用就好了。這樣也方便他們使用…
好好,我們現在來說 **Pattern **對象。那什麼是 Pattern 對象呢?如果你仔細看之前我寫的方法的話,你會發現幾乎每個正則的方法裏都有一個 Pattern 參數 ,回想一下那個參數寫的什麼? 對,Pattern對象就是我們編譯後的正則規則。
之前我們在講 re.compile()
方法的時候說過,這個方法返回的就是一個 Pattern對象。這個對象幾乎可以執行 re 中的所有方法。是比較簡單的一個對象。相信大家都可以理解。我覺得也沒啥好說的了。
4.4 re 中的 Match 對象
Emmn….說完了 Pattern對象我們現在來說說 Match 對象。
好的好的,我一定好好說這個….這個Match 對象呢,是 re構建的一個對象。我們通過 match()
, fullmatch()
, search()
,fjinditer()
,這幾種方法返回的都是 Match 對象。我們來看看一個 Match 對象中都包含什麼:
<_sre.SRE_Match object; span=(3, 6), match='abc'>
- span 指的是匹配到的字符串在字符串中的位置。例中的 3,6 分別是開始位置和結束位置。
- match 指的是匹配到的內容
下面我們來說說 Match 對象中常用的方法:
方法 | 解釋 |
---|---|
end([group]) | 獲取組的結束位置 |
expand(string) | 將match對象中的字符串替換成指定字符串 |
group(index) | 某個組匹配的結果 |
groupdict() | 返回組名作爲key,每個分組的匹配結果作爲value的字典 |
groups() | 所有分組的匹配結果,每個分組組成的結果以列表返回 |
start([group]) | 獲取組的開始位置 |
span([group]) | 獲取組的開始和結束位置 |
這就是 Match 對象的所有內容了。還是很容易掌握的嘛。
4.5 re 中的分組使用
說完了 Match 對象,我們自然要說道 正則中的 分組使用。這一部分只是對 Match
對象的補充與強調。我想如果會的人不用我來強調也會明白如何使用的。首先我們來舉個例子,分組查找出一些內容:
import re
string = "2019-05-01是勞動節"
result = re.search(r'(?P<year>\d{4}).(?P<month>\d+)', string)
print(result.groups())
print(result.group('year'), result.group(1))
print(result.group('month'), result.group(2))
運行結果:
('2019', '05')
2019 2019
05 05
可以看的到,我在分組的時候使用瞭如下的格式:
(?P<name>partten)
在前面我們說了一些關於分組的東西比如說 (?=partten)
、(?:partten)
、(?!partten)
等等諸如此類的元字符。但唯獨沒有說這個。
因爲他是 Python中專門的一種格式。<name>
用於給組命名。類似於我們引用庫時 as的用法。上面的例子中我們也展示了他的用法。當我們使用某個命名了的租的時候。可以直接使用 group('groupname')
的方法。
這樣做的好處顯而易見,當我們分了多個組的時候,可以不通過排序來提取內容,直接使用組名更加的簡單方便。
5. 正則表達式所有元字符的示例
講完了正則表達式了,也講完了python中的 re庫,下面我們來將之前說好的元字符的示例逐一展示出來:
5.1 基本元字符的示例
字符 | 描述 | |
---|---|---|
. | 匹配除換行符外的任意字符 | |
* | 匹配0個或多個符合表達式的字符 | |
? | 匹配0個或1個符合表達式的字符 | |
+ | 匹配1個或多個符合表達式的字符 | |
[…] | 匹配方括號內的任意字符 | |
[^…] | 匹配除方括號字符內的任意字符 |
元字符 “.” 的示例:
import re
string = "abcdefghigklmnopqrstuvwxyz1234567890"
result = re.match(r".bc", string)
print(result.group())
運行結果:
abc
元字符 “*” 的示例:
import re
string = "abcdefghigklmnopqrstuvwxyz1234567890"
result = re.search(r"(a.*g)", string)
print(result.group())
運行結果:
abcdefg
元字符 “?” 的示例:
import re
string = "aaabcdefg"
result = re.search(r"a?b", string)
print(result.group())
運行結果:
ab
元字符 “+” 的示例:
import re
string = "aaabcdefg"
result = re.search(r"a+", string)
print(result.group())
運行結果:
aaa
元字符 “[…]” 的示例:
import re
string = "aaabcdefg123456789"
result = re.findall(r"[\D]+", string)
print(result)
運行結果:
['aaabcdefg']
元字符 “[^...]
” 的示例:
import re
string = "aaabcdefg123456789"
result = re.findall(r"[^\d]+", string)
print(result)
運行結果:
['aaabcdefg']
5.2 轉義元字符的示例
字符 | 描述 |
---|---|
\ | 用於轉義下一個字符 |
\A | 僅匹配字符串的開頭 |
\b | 匹配一個單詞的邊界 |
\B | 匹配非單詞邊界 |
\cX | 匹配指明的控制字符 |
\d | 匹配一個數字,等價於[0-9] |
\D | 匹配一個非數字,等價於0-9 |
\f | 匹配一個換頁符,等價於\x0c和\cL |
\n | 匹配一個換行符,等價於\x0a和\cJ |
\t | 匹配一個製表符,等價於\x09和\cI |
\r | 匹配一個回車符,等價於\x0d和\cM |
\s | 匹配 一個不可見的字符 |
\S | 匹配一個可見的字符 |
\v | 匹配一個垂直製表符,等價於\x0b和\cK |
\w | 匹配包括下劃線的任何單詞字符 |
\W | 匹配任何非單詞字符 |
\Z | 僅匹配字符串的末尾 |
\xn | 匹配轉移爲\xn的十六進制轉義字符 |
\un | 匹配一個Unicode轉義字符 |
元字符 “\” 的示例:
import re
string = "aaabcdefg123456789"
result = re.findall(r"\d+", string)
print(result)
運行結果:
['123456789']
元字符 “\A” 的示例
import re
string = "aaabcdefg123456789"
result = re.findall(r"\A\w", string)
print(result)
運行結果:
['a']
元字符 “\b” 的示例
import re
string = "aaabcdefg123456789"
result = re.findall(r"\b\w{5}", string)
print(result)
運行結果:
['aaabc']
元字符 “\B” 的示例
import re
string = "I'm the king of the world"
result = re.findall(r"\B\w+", string)
print(result)
運行結果:
['he', 'ing', 'f', 'he', 'orld']
元字符 “\d” 的示例
import re
string = "abcdefg1234567"
result = re.findall(r"\d+", string, re.S)
print(result)
運行結果:
['1234567']
元字符 “\D” 的示例
import re
string = "abcdefg1234567"
result = re.findall(r"\D+", string, re.S)
print(result)
運行結果:
['abcdefg']
元字符 “\f” 的示例
import re
string = "abcdefg\f1234567"
result = re.findall(r"\f", string, re.S)
print(result)
運行結果:
['\x0c']
元字符 “\n” 的示例
import re
string = "abcdefg\n1234567"
result = re.findall(r"\n\d+", string, re.S)
print(result)
運行結果:
['\n1234567']
元字符 “\t” 的示例
import re
string = "abcdefg\t1234567"
result = re.findall(r"\t", string, re.S)
print(result)
運行結果:
['\t']
元字符 “\r” 的示例
import re
string = """abcdefg\r1234567"""
print(string)
result = re.findall(r"\r", string, re.S)
print(result)
運行結果:
1234567
['\r']
元字符 “\s” 的示例
import re
string = """abcdefg\n1234567"""
result = re.findall(r"\s", string, re.S)
print(result)
運行結果:
['\n']
元字符 “\S” 的示例
import re
string = """abcdefg\n1234567"""
result = re.findall(r"\S+", string, re.S)
print(result)
運行結果:
['abcdefg', '1234567']
元字符 “\w” 的示例
import re
string = """I_m the king of the world"""
result = re.findall(r"\w+", string, re.S)
print(result)
運行結果:
['I_m', 'the', 'king', 'of', 'the', 'world']
元字符 “\W” 的示例
import re
string = """I'm the king of the world"""
result = re.findall(r"\W+", string, re.S)
print(result)
運行結果:
["'", ' ', ' ', ' ', ' ', ' ']
元字符 “\Z” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"\w{2}\Z", string, re.S)
print(result)
運行結果:
['yz']
元字符 “\xn” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"\x77", string, re.S)
print(result)
運行結果:
['w']
元字符 “\un” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"\u0077", string, re.S)
print(result)
運行結果:
['w']
5.3 限定類元字符的示例
字符 | 描述 |
---|---|
{n} | 匹配確定的n個字符 |
{n,} | 匹配至少n個字符 |
{n,m} | 匹配至少n個字符,至多m個字符 |
| | 或符號,用於連接多個表達式 |
- | 範圍符,只能用於方括號中,如[a-z] |
^ | 匹配字符串的開頭,在方括號中表示取反 |
$ | 匹配字符串的末尾 |
(parameter) | 將括號內的內容進行匹配並分組 |
(?!parameter) | 正向否定預查,匹配不是parameter的內容 |
(?=parameter) | 正向肯定預查,匹配是parameter的內容 |
(?:parameter) | 匹配parameter獲取匹配結果 |
(?<=parameter) | 反向肯定預查,在任何匹配的paraemter處開始匹配字符串 |
(?<!parameter) | 反向否定預查,在任何不符合parameter的地方開始匹配字符串 |
元字符 “{n}” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"u\w{2}", string, re.S)
print(result)
運行結果:
['uvw']
元字符 “{n,}” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"u\w{3,}", string, re.S)
print(result)
運行結果:
['uvwxyz']
元字符 “{n,m}” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"u\w{1,3}", string, re.S)
print(result)
運行結果:
['uvwx']
元字符 “|” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"u\w|x\w", string, re.S)
print(result)
運行結果:
['uv', 'xy']
元字符 “-” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"[a-z]+", string, re.S)
print(result)
運行結果:
['uvwxyz']
元字符 “^” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"[^1-9]+", string, re.S)
print(result)
運行結果:
['uvwxyz']
元字符 “$” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"\w{3}$", string, re.S)
print(result)
運行結果:
['xyz']
元字符 “(parameter)” 的示例
import re
string = """uvwxyz"""
result = re.findall(r"u(\w{2})x(\w{2})", string, re.S)
print(result)
運行結果:
[('vw', 'yz')]
元字符 “(?=parameter)” 的示例
import re
string = """COD4 COD6 COD8"""
result = re.findall(r"COD.(?=4)", string, re.S) #匹配.=4的字符串COD
print(result)
運行結果:
['COD4']
元字符 “(?!parameter)” 的示例
import re
string = """COD4 COD6 COD8 """
result = re.findall(r"COD.(?!4)", string, re.S) # 匹配 .!=4的字符串
print(result)
運行結果:
['COD6', 'COD8']
元字符 “(?:parameter)” 的示例
import re
string = """COD4COD6COD8"""
result = re.findall(r".*?(?:4)", string, re.S) # 匹配獲取參數爲4的字符串
print(result)
運行結果:
['COD4']
元字符 “(?<=parameter)” 的示例
import re
string = """COD4 COD6 COD8 """
result = re.findall(r"COD.(?<=4)", string, re.S) # 從右向左匹配.=4的字符串
print(result)
運行結果:
['COD4']
元字符 “(?<!parameter)” 的示例
import re
string = """COD4 COD6 COD8 """
result = re.findall(r"COD.(?<!4)", string, re.S) # 從右向左匹配.!=4的字符串
print(result)
運行結果:
['COD6', 'COD8']
到這裏我們就把正則的所有元字符的示例就都演示完了。我所演示的是最基礎的用法,如果真的想要使用高級的用法,還是要自己多進行嘗試,多練習。練習的多了使用正則纔會信手拈來。
下期預告
你看你說了這麼多,又是訪問網頁,又是獲取數據的,說了半天我們還是不知道怎麼用。我會只是單獨的給你們說這麼簡單嗎?當然會有練習的啦。敬請期待下一期:Python 爬蟲十六式 - 第八式:實例解析-全書網!我們通過全書網來讓你一下吸收前面的知識。
好了,這就是今天的內容了,不知道你又學會了多少呢?我是Connor,一個從無到有的技術小白,願你在前進的道路上堅持不懈!
學習一時爽,一直學習一直爽!
系列文章
Python 爬蟲十六式 - 第一式:HTTP協議 >>>
Python 爬蟲十六式 - 第二式:urllib 與 urllib3 >>>
Python 爬蟲十六式 - 第三式:Requests的用法 >>>
Python 爬蟲十六式 - 第四式: 使用Xpath提取網頁內容 >>
Python 爬蟲十六式 - 第五式:BeautifulSoup-美味的湯 >>>
Python 爬蟲十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬蟲十六式 - 第八式:實例解析 - 全書網 >>>