細(xì)說python正則表達(dá)式

python的re模塊--細(xì)說正則表達(dá)式

可能是東半球最詳細(xì)最全面的re教程,翻譯自官方文檔,因?yàn)楣俜轿臋n寫的是真的好,所以,盡量保持原本的東西,做最少的改動,保持原汁原味!干貨十足。如果能耐著性子看完,一定會感覺編程能力上一個(gè)新的檔次!
大家有空也可以到我的個(gè)人博客上看看

前言

什么是正則表達(dá)式,regular expression在英語中是有規(guī)則的表達(dá)式,也就是說,該表達(dá)式只是一條的規(guī)則,而正則表達(dá)式引擎能夠根據(jù)這條規(guī)則,幫你在字符串中尋找所有符合規(guī)則的部分,比如,我有一條字符串"hello world 123"
而規(guī)則,可以很具體,比如hello 那么引擎會幫你把hello從字符串中找出來,規(guī)則也可以比較抽象,比如\d這個(gè)表示數(shù)字,也就是引擎會幫你把字符串中的數(shù)字尋找出來,根據(jù)正則表達(dá)式的理論,我們可以把規(guī)則串聯(lián)起來,變成一條更復(fù)雜的規(guī)則。想要從一條字符串中找到對你來說有意義的部分,你的任務(wù)可能不僅僅是從字符串中提取數(shù)字這么簡單,因此需要設(shè)計(jì)非常復(fù)雜的規(guī)則,而本教程則會告訴你,如何去制定非常那些非常復(fù)雜的規(guī)則。事實(shí)上,制定好規(guī)則之后,引擎不僅能夠根據(jù)規(guī)則進(jìn)行查找,還可以進(jìn)行分割,替換等復(fù)雜的操作,能讓你隨心所欲得處理字符串。本教程默認(rèn)你懂得一些python的簡單語句尤其是對str處理時(shí)的語句。

python中的re模塊提供無比強(qiáng)大的正則表達(dá)式功能。如果需要更加逆天的正則表達(dá)式功能,第三方regex模塊具有與標(biāo)準(zhǔn)庫re模塊兼容的API,但提供了額外的功能和更全面的Unicode支持。不過python自帶的re模塊已經(jīng)足夠牛逼了,只要你能夠掌握下面的內(nèi)容。

學(xué)習(xí)正則表達(dá)式的意義

為什么要學(xué)習(xí)正則表達(dá)式?不僅python,絕大多數(shù)語言的正則表達(dá)式的設(shè)計(jì)理念都是差不多的,你在這里學(xué)熟練了,那么對于其他語言的正則表達(dá)式也會融會貫通。學(xué)會正則表達(dá)式,不僅能提升編程能力,還能讓你的工作和生活更加方便。比如,有人在處理文章的時(shí)候,想把文章的數(shù)字標(biāo)號"第1章"變成漢字?jǐn)?shù)字編號"第一章",如果章節(jié)非常多,手動修改是非常痛苦的,而word自帶的替換功能已經(jīng)無能為力了,那么可以考慮使用一個(gè)功能強(qiáng)大的編輯器比如sublime然后通過正則表達(dá)式進(jìn)行替換。類似的文本處理在工作和生活中經(jīng)常出現(xiàn),掌握正則表達(dá)式絕對會讓你能夠更加輕松應(yīng)對這些場景。此外,學(xué)會正則表達(dá)式能夠做的東西非常多,也非常酷,比如本教程最后講的分詞器,就是編寫編譯器的關(guān)鍵步驟,沒錯(cuò),學(xué)完正則表達(dá)式,是開發(fā)一個(gè)屬于你自己的語言的關(guān)鍵步驟,說不定你的語言能夠和python一樣廣受歡迎。

關(guān)于\

正則表達(dá)式使用反斜線字符 \ 作為Escape符,讓特殊字符失去含義。這與Python在字符串的使用相沖突,所以,如果我們需要匹配反斜杠,我們需要輸入的正則表達(dá)式應(yīng)該是\\也就是我們要輸入兩個(gè)反斜杠,這樣一來,要告訴python的字符串,這里有兩個(gè)反斜杠,那么,我們的python字符串里面就需要有四個(gè)反斜杠\\\\

!!!如果你覺得很難理解,沒關(guān)系,只要記得下面的解決方案就可以了。

解決方案是將Python的原始字符串表示法用于正則表達(dá)式模式;在以 'r' 為前綴的字符串文字中不以任何特殊方式處理反斜杠。所以 r"\n" 是包含 \ 和 'n' 的兩個(gè)字符的字符串,而 "\n" 是包含換行符的單字符字符串。re讀取了 \n 連續(xù)兩個(gè)字符,他就知道,這種表達(dá)式是要匹配一個(gè)換行符。總而言之,使用的時(shí)候一定要使用r'str'這樣的模式,你在查找資料的時(shí)候看到也經(jīng)常是這種情況。

!!!以上內(nèi)容看不懂沒關(guān)系,只要記住,python的正則表達(dá)式都是 r"str" 這樣的形式就可以了!

正則表達(dá)式規(guī)則

規(guī)則是相通的,除了python,其他語言的正則表達(dá)式規(guī)則基本上都是一樣的,只是語言的實(shí)現(xiàn)不一樣,因此,我們先看看什么是正則表達(dá)式規(guī)則。

正則表達(dá)式(RE)實(shí)際上就是一條規(guī)則,能讓字符串的某些部分匹配上,或者說該部分符合這條規(guī)則。有時(shí)候規(guī)則比較具體,如找出字符串中所有的hello,那么就字符串中一定要是要有'hello'才能匹配,其他的'Hello',或者'HELLO',再或者有錯(cuò)別字的'gello'都是不符規(guī)則的。有時(shí)候呢,可以放寬一些規(guī)則,比如允許第一個(gè)字母是錯(cuò)別字,只要是小寫字母就好了,那么就可以這樣寫規(guī)則[a-z]ello,其中 [a-z]表示a到z中的任意一個(gè)。我們可以看到,在規(guī)則[a-z]ello中既有ello這樣的普通字符,又有[a-z]這樣的特殊規(guī)則,組成了一條完整的規(guī)則,我們也能看出,只有普通字符那么規(guī)則是非常具體嚴(yán)格的,加上[a-z]這樣的特殊符號之后規(guī)則變得更加靈活。所以,特殊符號才是正則表達(dá)式強(qiáng)大所在,我們可以使用不同的特殊符號進(jìn)而構(gòu)造更加復(fù)雜的規(guī)則。
下面就看看python的正則表達(dá)式有哪些特殊符號。(其他的語言中的特殊符號也都大同小異)

值得說明一下的是,下面的規(guī)則有些非常復(fù)雜,很多人可能這輩子都用不上,所以,看到你覺得復(fù)雜或者看不懂的規(guī)則,你可以跳過去,沒有任何影響,或許你在看完全部的內(nèi)容之后再回過來看這些特殊的符號,特殊的規(guī)則,會有新的領(lǐng)悟,所以,學(xué)習(xí)正則表達(dá)式不可能一下就能學(xué)會的,一定要多看幾遍,多練習(xí),才能融會貫通,因?yàn)閷W(xué)習(xí)最主要的還是要運(yùn)用。

特殊符號(又稱特殊字符)如下:

.

在默認(rèn)模式下,它匹配除換行符以外的任何字符。(如果已經(jīng)指定了DOTALL標(biāo)志,則它匹配包括換行符的任何字符。至于如何設(shè)定這個(gè),我們之后再講)

因此我們的設(shè)定規(guī)則 .ello 的時(shí)候,符合這條規(guī)則的字符串有'hello','8ello','kello'...非常多,不管開頭是什么字符,只要后面是'ello'就符合規(guī)則。

^

匹配字符串的開頭,并且在MULTILINE模式下,也會在每個(gè)換行符后立即匹配。

我們設(shè)定規(guī)則^hello 那么字符串 'hello world'里面的'hello'能夠符合規(guī)則,而'world hello'里面的'hello'不符合規(guī)則,因?yàn)樗辉谧址拈_頭。
由于在Multiline模式下,也匹配每個(gè)換行符后,所以,在開啟了multiline模式下,
'world\nhello'中的'hello'也符合規(guī)則。

$

匹配字符串的結(jié)尾或緊挨在字符串末尾的換行符之前,并且在MULTILINE模式下也匹配換行符之前的匹配。

foo 匹配 'foo' 和 'foobar' ,而正則表達(dá)式 foo$ 只匹配 'foo'或者'hello foo'。 更有趣的是,在 "foo1\nfoo2\n" 中搜索 foo.$ 通常與 'foo2' 匹配,而在MULTILINE模式下得到的是'foo1'。在'foo\n'中搜索單個(gè) $ 將會找到兩個(gè)(空)匹配:一個(gè)位于換行符之前,另一個(gè)位于字符串末尾。

*

*前面的RE匹配0次或多次,并盡可能多的重復(fù)。

我們設(shè)定規(guī)則 ab* ,意思是對b可以重復(fù)匹配。 將匹配'a','ab'或'a'后跟任意數(shù)量的'b'。而且*默認(rèn)的是貪婪模式,也就是說對于字符串'abbb'規(guī)則認(rèn)為整個(gè)字符串符合規(guī)則,而不是子字符串符合規(guī)則。將來我們要求返回符合規(guī)則的部分時(shí),將會返回整個(gè)字符串a(chǎn)bbb而不僅僅是ab或者a。

+

對于'+'前面的RE進(jìn)行一個(gè)或多個(gè)重復(fù)。

ab+ 將與 'ab' 匹配,后面還可以跟任意個(gè)'b' ,但這條規(guī)則不會匹配 'a' 。值得注意的是,這條規(guī)則也是貪婪的。

?

? 前面的RE進(jìn)行0或1個(gè)重復(fù)。

ab? 將匹配 'a' 或 'ab'。這條規(guī)則也是貪婪的。

*?, +?, ??

*, +? 限定符都是貪婪的; 它們匹配盡可能多的文本。 有時(shí)候這種行為是不希望的; 如果RE <.*>與 '<a>b<c>' 匹配,它將匹配整個(gè)字符串,而不僅僅是 '<a>' 。 添加? 在限定符之后以非貪婪或最小方式進(jìn)行匹配; 盡可能少的字符將被匹配。 使用RE <.*?>將只匹配 '<a>'。

值得一提的是,我們看到 a*? 我們應(yīng)當(dāng)想到這是一條關(guān)于*的規(guī)則,而不是關(guān)于?的規(guī)則,后者只是為前者服務(wù)的。

{m}

指定應(yīng)該匹配{m}前一個(gè)RE的正好m個(gè)副本; 更少的匹配導(dǎo)致整個(gè)RE不匹配。 例如, a{6} 將完全匹配六個(gè) 'a' 字符,但不是五個(gè)。
如果多于6次,那么多出的部分被忽略。

{m,n}

使得到的RE匹配前面RE的m到n次重復(fù),試圖盡可能多地匹配重復(fù)。 例如, a{3,5} 將匹配3到5個(gè) 'a' 字符。省略m則指定零作為下限,省略n指定無限上限。 例如, a{4,}b 會匹配 'aaaab' 或一千個(gè) 'a' 字符,后跟一個(gè) 'b',但不匹配 'aaab'。 逗號不能省略,否則修飾符會與之前描述的表單混淆。

這條規(guī)則也是貪婪的,它會盡可能到達(dá)重復(fù)n次

{m,n}?

使得到的RE從m到n重復(fù)前面的RE,嘗試匹配盡可能少的重復(fù)。 這是以前限定符的非貪婪版本。 例如,在6個(gè)字符的字符串'aaaaaa'上, a{3,5}將匹配5個(gè) 'a'字符,而 a{3,5}? 只會匹配3個(gè)字符 'a'。

\

要么特殊字符失去意義,要么標(biāo)志一個(gè)特殊序列; 之后會討論特殊序列。

首先,你在python中寫規(guī)則的時(shí)候一定要用raw字符串,也就是r'...',否則就會被''的特殊性給繞暈。其次要記住,這個(gè)符號叫Escape符號,使得特殊符號逃離特殊功能,比如,我們要匹配一個(gè)'.',那么我們寫規(guī)則的時(shí)候就需要這樣寫 r'\.'

[]

用于指示一組(或者說一個(gè)集合)字符。在一組中:

  • 字符可以單獨(dú)列出,例如 [amk] 將匹配 'a','m'或'k'。
  • 字符的范圍可以通過給出兩個(gè)字符并用'-'分隔來指示,例如 [a-z]將匹配任何小寫ASCII字母, [0-5][0-9] 將匹配所有的兩位數(shù)字00到59, [0-9A-Fa-f] 將匹配任何十六進(jìn)制數(shù)字。如果 - 被轉(zhuǎn)義(例如[a\-z]),或者被放置為第一個(gè)或最后一個(gè)字符(例如 [-a][a-] ),它將匹配一個(gè)'-'。
  • 特殊字符在集合內(nèi)部失去其特殊含義。例如, [(+*)]將匹配任何文字字符 '(' , '+' , '*' 或 ')' 。
  • 字符類如 \w\S 也可以在一個(gè)集合內(nèi)接受,盡管它們匹配的字符取決于ASCII或LOCALE模式是否有效。(之后會有關(guān)于字符類更加詳細(xì)的說明)
  • 不在一個(gè)范圍內(nèi)的字符可以通過對該集合進(jìn)行補(bǔ)充來匹配。如果[]中的第一個(gè)字符是 '^' ,則不匹配的所有字符將被匹配。例如,[^5]將匹配除'5'以外的任何字符,并且[^^]將匹配除 '^' 以外的任何字符。 '^' 如果它不是集合中的第一個(gè)字符,它沒有特別的意義。
  • 在一個(gè)集合內(nèi)匹配']',在它之前加一個(gè)反斜杠,或者將它放在集合的開頭。例如, [()[]{}]和[{}]都將與括號匹配。

|

A|B,其中A和B可以是任意RE,創(chuàng)建一個(gè)匹配A或B的正則表達(dá)式。任意數(shù)量的RE可以用'|'分隔。通過這種方式,這可以在組內(nèi)使用(見下文)。當(dāng)目標(biāo)字符串被掃描時(shí),由'|'分隔的RE 從左到右嘗試。當(dāng)一個(gè)模式完全匹配時(shí),該分支被接受。 這意味著一旦A匹配,B將不會被進(jìn)一步測試,即使它會產(chǎn)生更長的整體匹配。 換句話說, '|'操作從不貪婪。要匹配'|',請使用 \| ,或?qū)⑵浞旁谧址愔?如[|]中所示

\d

對于Unicode(str)模式:

  • 匹配任何Unicode十進(jìn)制數(shù)字。這包括[0-9]以及許多其他數(shù)字字符。 如果使用ASCII標(biāo)志,則只匹配[0-9]。

對于8位(字節(jié))模式:

  • 匹配任何十進(jìn)制數(shù)字; 這相當(dāng)于[0-9]。

\D

匹配任何不是十進(jìn)制數(shù)字的字符。 這與\d相反。 如果使用ASCII標(biāo)志,則相當(dāng)于[^ 0-9]。

\s

對于Unicode(str)模式:

  • 匹配Unicode空白字符(包括[\t \n \r \f \v]以及許多其他字符,例如許多語言中的排版規(guī)則強(qiáng)制的非空白空格)。 如果使用ASCII標(biāo)志,則只匹配[\t \n \r \f \v]。

對于8位(字節(jié))模式:

  • 在ASCII字符集中匹配被認(rèn)為是空白的字符; 這相當(dāng)于[\t \n \r \f \v]。

\S

匹配任何不是空白字符的字符。 這與\s相反。 如果使用ASCII標(biāo)志,則這等價(jià)于[^ \t \n \r \f \v]。

\w

對于Unicode(str)模式:

  • 匹配Unicode字符; 這包括大多數(shù)可以是任何語言的單詞的一部分的字符,以及數(shù)字和下劃線。 如果使用ASCII標(biāo)志,則只匹配[a-zA-Z0-9_]。

對于8位(字節(jié))模式:

  • 匹配ASCII字符集中被認(rèn)為是字母數(shù)字的字符; 這相當(dāng)于[a-zA-Z0-9_]。 如果使用LOCALE標(biāo)志,則匹配在當(dāng)前語言環(huán)境和下劃線中被認(rèn)為是字母數(shù)字的字符。

\W

匹配任何不是單詞字符的字符。 這與\ w相反。 如果使用ASCII標(biāo)志,則變成等效于[^ a-zA-Z0-9_]。 如果使用LOCALE標(biāo)志,則匹配在當(dāng)前語言環(huán)境和下劃線中被認(rèn)為是字母數(shù)字的字符。

\Z

只匹配字符串的末尾。只能放在正則表達(dá)式末尾

進(jìn)階規(guī)則

(...)

匹配括號內(nèi)的任何正則表達(dá)式,并指示組的開始和結(jié)束; 在匹配完成后可以檢索組的內(nèi)容,并且可以在后面的字符串中使用\number特殊序列進(jìn)行匹配,之后有詳細(xì)描述。 要匹配'('或')',請使用 \(\) ,或?qū)⑺鼈兎旁谧址愔? [(],[]] 。 舉個(gè)例子我有個(gè)字符串 'asdfa020-12345678bsaefga' 可以看到我們的電話號碼被一群字母圍住了,怎么把電話號碼拿出來,順便把區(qū)號和真正的號碼都分開呢? 可以用這樣的規(guī)則(\d{4})-(\d)* 這樣符合結(jié)果字符串就是 '020-12345678' 但是,返回的結(jié)果對象并不僅僅是一個(gè)字符串,他還有.group,其中 group(1) = '020' group(2)='12345678'

\number

匹配相同編號的組的內(nèi)容。 例如 (.+) \1 這個(gè)式子等價(jià)于 (.+) (.+) 和'the the' 或 '55 55'匹配,但不匹配'thethe'(注意組之后的空格)。 該特殊序列只能用于匹配前99個(gè)組中的一個(gè)。 如果數(shù)字的第一個(gè)數(shù)字是0或數(shù)字是3個(gè)八進(jìn)制數(shù)字長度,則不會將其解釋為組匹配,而是將其解釋為具有八進(jìn)制數(shù)值的字符(這句話初學(xué)者可以略過)。

這個(gè)規(guī)則非常棒,能幫你省不少事情,比如一個(gè)字符串里面有兩個(gè)郵件地址,你想把它們都找出來,你寫好了一個(gè)檢測郵件地址規(guī)則,剩下那個(gè)你不想寫了,你可以引用一下就可以了。

溫馨提示,下面的特殊符號可能比較難,需要耐心去體會,或者干脆略過。

(?P<name>...)

與常規(guī)圓括號類似,但可以通過符號組名稱來訪問與該組匹配的子字符串。 組名稱必須是有效的Python標(biāo)識符,并且每個(gè)組名稱只能在正則表達(dá)式中定義一次。

相當(dāng)于給我們之前提到的組(...)添加了一個(gè)名字而已,添加名字的好處是可以根據(jù)名字來引用這個(gè)組,而不是靠編號。
相當(dāng)于給我們之前提到的組(...)添加了一個(gè)名字而已,添加名字的好處是可以根據(jù)名字來引用這個(gè)組,而不是靠編號。舉個(gè)之前的例子,有字符串 'asdfa020-12345678bsaefga' ,我想取出號碼,可以這樣寫
(?P<quhao>\d{4})-(?P<haoma>\d*)
這樣返回的結(jié)果字符串就是020-12345678,但是返回的對象還有g(shù)roup屬性,而且 group['quhao'] = 020 group['haoma'] = 12345678

命名組可以在三種情況下被引用。 比如我們想在句子中找到引號,這樣我們就能找到文章中引用的內(nèi)容,所以,我們可以這樣寫 (P<quote>['"]) 這樣就能找到一個(gè)單引號或者雙引號,但是一般來說,我們需要找到一對單引號或者雙引號,這時(shí)我們不必在寫一個(gè)單引號或雙引號的正則表達(dá)式,只要引用一下之前的就好了,如(?P<quote>['"]).*?(?P=quote) 后面一個(gè)實(shí)際上就是對于前面的引用,這時(shí)再正則表達(dá)式中引用的情況,也就是下表中第一種情況。實(shí)際上不僅規(guī)則中很有可能會引用前面寫好的組,還有其他的兩種情況需要引用,請見表

引用'quote' 引用的方法
在正則表達(dá)式中 (?P=quote)
\1
在匹配的結(jié)果中 m.group('quote')
m.end('queto')
在re.sub中的repl里 \g<quote>
\g<1>
\1

(?P=name)

對指定組的反斜線引用; 它匹配與早先的組命名相匹配的任何文本。見上一條的說明

(?...)

這是一個(gè)擴(kuò)展符號。'?'后面的第一個(gè)字符決定了結(jié)構(gòu)的含義和進(jìn)一步語法。擴(kuò)展通常不會創(chuàng)建一個(gè)新的組。(?P<name> ...)是這個(gè)規(guī)則的唯一例外,以下是當(dāng)前支持的擴(kuò)展。

(?aiLmsux)

(?號后面跟著'a','i','L','m','s','u','x'的一個(gè)或多個(gè)字母)。這些字母設(shè)置相應(yīng)的標(biāo)志:re.A(僅ASCII匹配),re.I(忽略大小寫),re.L(依賴于語言環(huán)境),re.M(多行),re.S(點(diǎn)匹配全部) ,re.U(Unicode匹配)和re.X(詳細(xì)),用于整個(gè)正則表達(dá)式。這些標(biāo)志的作用在模塊內(nèi)容中有具體的描述。事實(shí)上,這些標(biāo)志可以通過flag參數(shù)傳給正則表達(dá)式,也可以像這樣直接寫進(jìn)正則表達(dá)式里面。

(?:...)

常規(guī)圓括號的非捕獲版本。 匹配括號內(nèi)的任何正則表達(dá)式,但匹配的子字符串在執(zhí)行匹配或稍后引用模式后無法檢索。

(?aiLmsux-imsx:...)

(來自'a','i','L','m','s','u','x'的零個(gè)或多個(gè)字母,可選地后面跟著' - ',后面跟著一個(gè)或多個(gè)來自'','m','s','x')。字母設(shè)置或刪除相應(yīng)的標(biāo)志:re.A(僅ASCII匹配),re.I(忽略大小寫),re.L(依賴于語言環(huán)境),re.M(多行),re.S(點(diǎn)全部匹配),re.U(Unicode匹配)和re.X(冗長),用于表達(dá)部分。 注意,這里上面提到的(?aiLmsux)的區(qū)別是,這里有一個(gè)冒號,后面跟正則表達(dá)式,因此這里設(shè)置的標(biāo)志僅適用于窄內(nèi)聯(lián)組,并且原始匹配模式在組外部恢復(fù)。在設(shè)置標(biāo)志的時(shí)候(?aiLmsux-imsx:...)等價(jià)于(?aLu:...),也就是沒必要刻意先在左邊寫進(jìn)去后邊再跟隨'-'號刪掉,但是,有時(shí)候'-'也是很有用的,比如之前設(shè)置了(?i)說明對所有的正則表達(dá)式都忽略大小寫,但是你對某一個(gè)部分需要強(qiáng)調(diào)大小寫,比如,你想要找superMAN對前面的super的大小寫無所謂,但要求MAN一定是大寫,可以這樣寫 (?i)super(?-i:MAN)這樣 sUpeRMAN能匹配,而superMan則不能匹配

(?#...)

一條評論; 圓括號的內(nèi)容被簡單地忽略。

(?=...)

如果...匹配next,但不消耗任何字符串。 這被稱為前瞻斷言。 例如, Isaac(?=Asimov) 只有跟隨著'Asimov'才會匹配'Isaac'。但返回的結(jié)果依然還是Isaac,所謂不消耗字符串,意思是,后面的Asimov依然可以被繼續(xù)匹配,如
Isaac(?=[A])AsimovAlab 匹配 'IsaacAsimovAlab'
Isaac(?=[Asimov])G 則會無法匹配任何字符串,因?yàn)楦鶕?jù)(?=...)要求,Issac后面必須是Asimov,而它后面又要跟著一個(gè)G,所以,互相矛盾。

(?!...)

如果...不匹配。 這是一個(gè)負(fù)面的前瞻斷言。 例如,Isaac(?!Asimov)只有在沒有跟隨'Asimov'時(shí)才會匹配'Isaac'。

(?<=...)

匹配如果字符串中的當(dāng)前位置在...之前匹配...,并以當(dāng)前位置結(jié)束。 這被稱后行斷言。 (?<= abc)def 會在'abcdef' 中找到一個(gè)匹配項(xiàng),因?yàn)閘ookbehind會回看3個(gè)字符并檢查包含的模式是否匹配。 包含的模式只能匹配一些固定長度的字符串,這意味著 abca|b 是允許的,但a*a{3,4}不是。 請注意,像這種向前看的搜索字符串模式在匹配開頭的時(shí)候會出現(xiàn)問題;因此 必須使用search()函數(shù)而不是match()函數(shù):仔細(xì)想想為什么?

    >>> import re
    >>> m = re.search('(?<=abc)def', 'abcdef')
    >>> m.group(0)
    'def'

本示例在連字符后面查找單詞:

    >>> m = re.search(r'(<=-)\w+', 'spam-egg')
    >>> m.group(0)
    'egg'

(?<!...)

上一例子的不匹配情況

(?(id/name)yes-pattern|no-pattern)

如果存在給定id或名稱的組,則嘗試與yes-模式匹配,如果不存在,則使用無模式。 無圖案是可選的,可以省略。 例如,正則表達(dá)式(<)?(\w+@\w+(?:\.\w +)+)(?(1)>|$)是一個(gè)電子郵件匹配模式,它將匹配'<user@host.com >'以及'user@host.com',但不匹配'<user@host.com'和'user@host.com>'。為什么,就是因?yàn)楹竺嬗幸粋€(gè)(?(1)>|$) 我們先看看其中的(1) 表示什么,表示正則表達(dá)式最前面的那個(gè)括號,也就是郵件地址匹配正則表達(dá)式中的(<) 如果這個(gè)括號匹配到了內(nèi)容,也就是檢查看到了郵件地址以'<'字符開始,那么,我們對于該郵件地址末尾字符的檢測就是>,如果(<)檢測失敗,也就是郵件地址不以'<'開頭,那么我們對于該郵件地址末尾字符的檢測就是'$'

\A

只匹配字符串的開頭。只能放在正則表達(dá)式開頭

\b

匹配空字符串,但僅限于單詞的開頭或結(jié)尾。一個(gè)單詞被定義為一個(gè)單詞字符序列。 請注意,在形式上,\b 被定義為 \w\W 字符之間的界限,或 \w 和字符串的開始/結(jié)尾之間的界限。這意味著r'\bfoo\b' 匹配 'foo' , 'foo.' , '(foo)' , 'bar foo baz' ,但不匹配'foobar'或'foo3'。

默認(rèn)情況下,Unicode字母數(shù)字是Unicode模式中使用的字母數(shù)字,但可以通過使用ASCII標(biāo)志來更改。如果使用LOCALE標(biāo)志,字邊界由當(dāng)前的區(qū)域設(shè)置確定。在字符范圍內(nèi),\ b代表退格字符,以便與Python中的字符串文字兼容。

\B

匹配空字符串,但僅限于它不在單詞的開頭或結(jié)尾。 這意味著r'py\B'匹配'python','py3','py2',但不匹配'py','py。'或'py!'。 \B\b相反,因此Unicode模式中的單詞字符(\w)是Unicode字母數(shù)字加下劃線。 如果使用LOCALE標(biāo)志,單詞的定義由當(dāng)前的區(qū)域設(shè)置確定。

正則表達(dá)式對象

我們寫好一條規(guī)則,如r'.ello',目前在python里面只是一個(gè)字符串,我們要讓這個(gè)規(guī)則字符串變成正則表達(dá)式,需要將這個(gè)字符串編譯一下,成為表達(dá)式對象,這個(gè)對象又稱pattern,即樣式。

re.compile(pattern, flags=0)

通過上面的模塊函數(shù),根據(jù)我們寫的規(guī)則字符串,生成一個(gè)正則表達(dá)式對象。

序列

    # regex = r'你的規(guī)則'
    pattern = re.compile(regex)   #這里 regex只是規(guī)則字符串,pattern才是正則表達(dá)式對象
    result = pattern.match(string)

等價(jià)于

    result = re.match(regex, string)

使用re.compile()生成的正則表達(dá)式對象可以重用,更高效。因此,推薦使用第一種用法,第二種方法明顯是在背后偷偷編譯了個(gè) pattern,只是為了方便,快捷地使用而已。

正則表達(dá)式對象支持以下方法

pattern.search(string[, pos[, endpos]])

掃描字符串查找第一個(gè)能匹配上pattern的部分,并返回相應(yīng)的匹配對象。 如果字符串中沒有位置與模式匹配,則返回None; 請注意,這與在字符串中的某處找到零長度匹配不同。如果你本身要匹配的就是一個(gè)空字符,如上述規(guī)則中的r'\b',那么返回一個(gè)""表示的是找到了對應(yīng)的r'\b',也就是找到了空字符,而返回None找不到任何匹配的空字符。

可選的第二個(gè)參數(shù)pos在搜索要開始的字符串中給出一個(gè)索引,它默認(rèn)為0,從指定的pos開始搜索,也就是說避開字符串開始的部分。pattern.search("hello world") 表示在 hello world 中查找
pattern.search("hello world",2) 表示在 llo world 中查找

可選參數(shù)endpos表示搜索字符串的截止位置,也就是說,你想避開字符串結(jié)尾的部分。只有從pos到endpos - 1的字符才會被搜索到。如果endpos小于pos,則不會找到匹配,否則pattern.search(string,0,50)等同于pattern.search(string[:50],0)。

    >>> pattern = re.compile("d")
    >>> pattern.search("dog")     # 能匹配
    <re.Match object; span=(0, 1), match='d'>
    >>> pattern.search("dog", 1)  # 不能匹配

我們可以看到,我們首先,寫好一個(gè)規(guī)則d,這是一個(gè)具體的規(guī)則,沒有用到任何特殊符號,接著,我們把這條規(guī)則編譯成pattern,這是一個(gè)正則表達(dá)式對象,然后使用pattern的search函數(shù),在字符串"dog"中搜索。匹配的結(jié)果返回一個(gè)Match對象,之后會詳細(xì)講解這個(gè)Match對象。

Pattern.match(string[, pos[, endpos]])

如果字符串開頭的零個(gè)或多個(gè)字符與此正則表達(dá)式匹配,則返回相應(yīng)的匹配對象。 如果字符串與模式不匹配,則返回None; 請注意,這與零長度匹配不同。

可選的pos和endpos參數(shù)與search()方法具有相同的含義。

    >>> pattern = re.compile(r"o")
    >>> pattern.match("dog")      # 不匹配,因?yàn)?"o" 不是 "dog"的最前面.
    >>> pattern.match("dog", 1)   # 能匹配  "o" 是 "dog" 的第二個(gè)字母.
    <re.Match object; span=(1, 2), match='o'>;

這個(gè)函數(shù)和search差不多,但是,規(guī)定一定要從起始位置就得匹配上,否則就不算匹配成功。比如,我們在"dog"中搜索o但是,由于開始的位置不是o所以匹配失敗。
如果您想在字符串中的任何位置找到匹配項(xiàng),請改用search()(另請參閱search()與match())。

pattern.fullmatch(string[, pos[, endpos]])

如果整個(gè)字符串匹配此正則表達(dá)式,則返回相應(yīng)的匹配對象。 如果字符串與模式不匹配,則返回None; 請注意,這與零長度匹配不同。

可選的pos和endpos參數(shù)與search()方法具有相同的含義。

    >>> pattern = re.compile("o[gh]")
    >>> pattern.fullmatch("dog")      # No match as "o" is not at the start of "dog".
    >>> pattern.fullmatch("ogre")     # No match as not the full string matches.
    >>> pattern.fullmatch("doggie", 1, 3)   # Matches within given limits.
    <re.Match object; span=(1, 3), match='og'>

這個(gè)函數(shù)規(guī)定整個(gè)字符串要跟pattern匹配上,才算匹配成功。

pattern.split(string, maxsplit=0)

平時(shí),如果我們想拆分一個(gè)字符串,python內(nèi)置的split函數(shù)需要寫入固定的分拆字符,比如下面的字符串"abc1efg1rgh" 我們可以以'1'字符來分拆這個(gè)字符串,但是萬一,分割字符串的不只是'1'而是其他數(shù)字怎么辦?如"abc1efg2hij3klm"python自帶的split就束手無策了。這時(shí)候就需要用到正則表達(dá)式的拆分了,你會發(fā)現(xiàn)解決這個(gè)問題是分分鐘的事。

    >>> pattern = re.compile(r'[0-9]')
    >>> pattern.split("abc1efg2hij3klm")
    ['abc', 'efg', 'hij', 'klm']

這個(gè)函數(shù)根據(jù)表達(dá)式規(guī)則分拆字符串,返回一個(gè)list。(溫馨提示,后面這點(diǎn)內(nèi)容可以跳過)如果在表達(dá)式中使用捕獲括號,則表達(dá)式中所有組的文本也會作為結(jié)果列表的一部分返回。 如果maxsplit不為零,則最多發(fā)生maxsplit分割,并且字符串的其余部分作為列表的最后一個(gè)元素返回。

    >>> pattern = re.compile(r'\W+')
    >>> pattern.split( 'Words,words,words.')
    ['Words', 'words', 'words', '']

    >>> pattern = re.compile(r'(\W+)')
    >>> pattern.split( 'Words, words, words.')
    ['Words', ', ', 'words', ', ', 'words', '.', '']
    
    >>> pattern = re.compile(r'\W+')
    >>> pattern.split('Words, words, words.', 1)
    ['Words', 'words, words.']

    >>> pattern = re.compile('[a-f]+',flags=re.IGNORECASE)
    >>> pattern.split( '0a3B9' )
    ['0', '3', '9']

如果分隔符中有捕獲組,并且它在字符串的起始處匹配,則結(jié)果將以空字符串開頭。 字符串的結(jié)尾也是一樣:

    >>> pattern = re.compile(r'(\W+)')
    >>> pattern.split( '...words, words...')
    ['', '...', 'words', ', ', 'words', '...', '']

這樣,分隔符組件總是在結(jié)果列表中的相同索引處找到。

pattern.findall(string[, pos[, endpos]])

返回字符串中模式的所有非重疊匹配項(xiàng),作為字符串列表。 字符串從左到右掃描,匹配按照找到的順序返回。 如果模式中存在一個(gè)或多個(gè)組,返回組列表; 如果模式有多個(gè)組,這將是一個(gè)元組列表。 結(jié)果中包含空匹配項(xiàng)。

比如,我們想要查找一個(gè)字符串里面全部的數(shù)字

    >>> pattern = re.compile(r'\d+')
    >>> pattern.findall("a12b56c54d89")
    ['12', '56', '54', '89']

如果有分組,比如,我們需要把查找的數(shù)字的末尾作為分組提取出來,那么返回的就是數(shù)字的最后一位

    >>> pattern = re.compile(r'\d*([0-9])')  
    >>> pattern.findall("a124b567c54d892")  
    ['4', '7', '4', '2']

r'\d*([0-9])' 這條規(guī)則本來匹配的是下面的123,567,54,892但是這里由于有了后面的括號,所以只返回括號里面的

如果有多個(gè)分組,則返回這些分組的tuple,比如,要返回?cái)?shù)字的個(gè)位數(shù)和十位數(shù)

    >>> pattern = re.compile(r'\d*([0-9])([0-9])')
    >>> pattern.findall("a124b567c54d892")
    [('2', '4'), ('6', '7'), ('5', '4'), ('9', '2')]

pattern.finditer(string[, pos[, endpos]])

返回一個(gè)迭代器,產(chǎn)生字符串中RE模式的所有非重疊匹配的匹配對象。 字符串從左到右掃描,匹配按照找到的順序返回。
和上面唯一的區(qū)別是返回的是一個(gè)iter而不是一個(gè)list,如果你對Python熟悉的話應(yīng)該了解這兩者的區(qū)別,如果不熟悉的話,建議使用上一種方法就好了,不要管這個(gè)。
接受可選的pos和endpos參數(shù),這些參數(shù)限制搜索區(qū)域,如search()。

pattern.sub(repl, string, count=0)

python自帶replace函數(shù)的加強(qiáng)版。
先通過正則表達(dá)式規(guī)則找到string中符合規(guī)則的部分,然后替換成repl
如果未找到能匹配規(guī)則的部分,則字符串將保持不變。 repl可以是一個(gè)字符串或一個(gè)函數(shù); 如果它是一個(gè)字符串,則處理其中的任何反斜杠轉(zhuǎn)義。 也就是\n被轉(zhuǎn)換為單個(gè)換行符,\r被轉(zhuǎn)換為回車符,等等。 像 & 一樣的未知轉(zhuǎn)義單獨(dú)保留。引用(例如\6)被替換為模式中由組6匹配的子字符串。 例如: 我們把分割字母的數(shù)字換成'000'

    >>> pattern = re.compile(r'\d+')
    >>> pattern.sub('000', '12abc34de56fg89')
    '000abc000de000fg000'

repl可以引用分組,因此,假如我們要把分割字母的數(shù)字換成他們的個(gè)位數(shù),可以這樣.
注意這里repl需要用raw字符串,否則re模塊無法識別'\1',還是那個(gè)''的問題。所以,能用raw盡量用raw

    >>> pattern = re.compile(r'\d*(\d)')
    >>> pattern.sub(r'\1','12abc34de56fg89') # 這里的r'\1'對應(yīng)的就是上面 r'\d*(\d)'中的(\d)
    '2abc4de6fg9'

如果repl是一個(gè)函數(shù),它會實(shí)現(xiàn)更加復(fù)雜的替換,比如我們需要把分割字母的數(shù)字翻倍。也就是,我們我們每個(gè)匹配到的數(shù)字,我們都要通過函數(shù)處理一下,以返回值作為repl 例如:

    >>> def func(n):
    ...           return str(int(n.group())*2)
    ...
    >>> pattern = re.compile(r'\d+')
    >>> pattern = re.sub(func,'12abc34de56fg89')
    '24abc68de112fg178'

注意到,傳給func的是一個(gè)個(gè)匹配對象(后面會講到),該對象包裝了真正匹配到的數(shù)字,如'12','34','56','89',所以使用了一個(gè) n.group()來提取數(shù)字。匹配對象的強(qiáng)大之處還在于我們不僅可以提取完整的匹配數(shù)字如'12',我們還可以提取分組,比如,我們在pattern中設(shè)定了個(gè)位數(shù)字作為組,那么就可以提取出來,如下面的例子,把找到的數(shù)字替換成該數(shù)字的個(gè)位數(shù)的兩倍

    >>> def func(n):
    ...   return str(int(n.group(1))*2)  
     # 對比上面n.group()其實(shí)是n.group(0)表示匹配的字符串,也就是整個(gè)r'\d*(\d)' 能匹配到的字符串
     # n.group(1) 對應(yīng)的就是 r'\d*(\d)' 中的 (\d)匹配到的東西
    ...
    >>> pattern = re.compile(r'\d*(\d)')
    >>> pattern.sub(func,'12abc34de56fg89')
    '4abc8de12fg18'

關(guān)于匹配對象,我們在后面還會有詳細(xì)的講解。

pattern.subn(repl, string, count=0)

執(zhí)行與sub()相同的操作,但返回一個(gè)元組(new_string,number_of_subs_made),也就是不僅返回一個(gè)和上面函數(shù)一樣的字符串,還多返回了一個(gè)數(shù)字,代表了總共替換的次數(shù),像上面的例子

    >>> pattern.subn(func,'12abc34de56fg89')
    ('4abc8de12fg18', 4)

正則表達(dá)式對象的屬性:

pattern.flags

正則表達(dá)式匹配標(biāo)志。在re.compile()函數(shù)中設(shè)定,比如要忽略大小寫

    >>> pattern = re.compile(r'd',flags=re.IGNORECASE)
    >>> pattern.findall('DOG')
    ['D']

如果要設(shè)置多個(gè)flags,可以用 | 隔開

  >>> pattern = re.compile(r'd',flags=re.IGNORECASE | re.MULTILINE)

當(dāng)然,設(shè)置flags也可以通過內(nèi)聯(lián)的方式

```python
  >>> pattern = re.compile(r'(?im)d')
  >>> pattern = re.compile(r'd',flags=re.IGNORECASE | re.MULTILINE)

上面兩者是等價(jià)的。

pattern.groups

查看模式中的捕獲組數(shù)量。也就是你在規(guī)則中使用捕獲括號的數(shù)量
比如 p = re.compile(r'hello(\d)') 那么這時(shí) p.groups 就是 1

pattern.groupindex

如果你在規(guī)則設(shè)定的時(shí)候使用了(?P<name>),那么這個(gè)變量可以返回該特殊符號使用的情況。
(?P<id>)定義的任何符號組名稱映射到組編號的字典。 如果模式中沒有使用符號組,則字典為空。

pattern.pattern

模式對象編譯的模式字符串。就是那個(gè)寫下的規(guī)則。

    >>> p = re.compile(r'(?im)d')
    >>> p.pattern
    '(?im)d'

re模塊自帶的函數(shù)

我們上面學(xué)習(xí)了使用正則表達(dá)式的兩個(gè)步驟,首先編譯出正則表達(dá)式對象pattern,然后,調(diào)用pattern的search,findall,match等函數(shù)進(jìn)行匹配等操作,實(shí)際上re直接提供了search,findall,match等快捷操作,允許我們直接操作,而不需先編譯pattern,而是直接把規(guī)則寫在操作函數(shù)之中。這樣的操作比上面提到的方法減少了一行,有了更好地便捷性,但是也犧牲了復(fù)用性等功能。

re.match(pattern, string, flags=0)

下面的兩個(gè)匹配操作是等價(jià)

>>> pattern = re.compile(r"o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> re.match(r"o","dog")      # No match as "o" is not at the start of "dog".

事實(shí)上,第二種直接操作在背后也是先用第一個(gè)參數(shù)編譯成正則表達(dá)式對象然后在讓這個(gè)對象調(diào)用match方法。其他的search,findall,split等操作也是一樣的。

re.search(pattern, string, flags=0)

參考pattern.search

re.fullmatch(pattern, string, flags=0)

參考pattern.full

re.split(pattern, string, maxsplit=0, flags=0)

參考pattern.split

re.findall(pattern, string, flags=0)

參考pattern.findall

re.finditer(pattern, string, flags=0)

參考pattern.finditer

re.sub(pattern, repl, string, count=0, flags=0)

參考pattern.sub

re.subn(pattern, repl, string, count=0, flags=0)

執(zhí)行與sub()相同的操作,但返回一個(gè)元組(new_string,number_of_subs_made)。

re.escape(pattern)

讓pattern中的特殊字符失去意義。 如果您想匹配任何可能具有正則表達(dá)式元字符的文字字符串,這非常有用。 例如:

  >>> print(re.escape('python.exe'))
  python\.exe
  >>> p = re.compile(re.escape(r'.'))  # 等價(jià)于 p = re.compile(r'\.')
  >>> p.search("abc")
  # 不能匹配
  >>> p.search(".")
  <_sre.SRE_Match object at 0x05370D08>

再來一個(gè)例子

  >>> operators = ['+', '-', '*', '/', '**']
  >>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))
  /|\-|\+|\*\*|\*

re模塊自帶的參數(shù)

re.A == re.ASCII

使\w,\W,\b,\B,\d,\D,\s和\S執(zhí)行僅ASCII匹配而不是完全Unicode匹配。 這只對Unicode模式有意義,并且在字節(jié)模式中被忽略。 對應(yīng)于內(nèi)聯(lián)標(biāo)志(?a)。

請注意,為了向后兼容,re.U標(biāo)志仍然存在(以及它的同義詞re.UNICODE及其嵌入對象(?u)),但這些在Python 3中是多余的,因?yàn)槟J(rèn)情況下匹配是Unicode的Unicode(和Unicode 字節(jié)不允許匹配)。

re.I == re.IGNORECASE

執(zhí)行不區(qū)分大小寫的匹配; 像[A-Z]這樣的表達(dá)式也將匹配小寫字母。 除非使用re.ASCII標(biāo)志來禁用非ASCII匹配,否則完全的Unicode匹配(例如ü匹配ü)也是有效的。 除非使用re.LOCALE標(biāo)志,否則當(dāng)前語言環(huán)境不會更改此標(biāo)志的效果。 對應(yīng)于內(nèi)聯(lián)標(biāo)志(?i)。

請注意,當(dāng)Unicode模式[a-z]或[A-Z]與IGNORECASE標(biāo)志組合使用時(shí),它們將與52個(gè)ASCII字母和另外4個(gè)非ASCII字母匹配:'?'(U + 0130,拉丁語大寫字母I與 (U + 0131,拉丁小字母無點(diǎn)i),'s'(U + 017F,拉丁小寫字母長)和'K'(U + 212A,開爾文符號)。 如果使用ASCII標(biāo)志,只匹配字母'a'到'z'和'A'到'Z'。

re.L == re.LOCALE

根據(jù)當(dāng)前語言環(huán)境,使\ w,\ W,\ b,\ B和不區(qū)分大小寫的匹配。 該標(biāo)志只能用于字節(jié)模式。 由于區(qū)域設(shè)置機(jī)制非常不可靠,因此不鼓勵使用此標(biāo)志,它一次只處理一種"文化",并且僅適用于8位語言環(huán)境。 對于Unicode(str)模式,默認(rèn)情況下,Unicode匹配已在Python 3中啟用,并且它能夠處理不同的語言環(huán)境/語言。 對應(yīng)于內(nèi)聯(lián)標(biāo)志(?L)。

在版本3.6中更改:re.LOCALE只能與字節(jié)模式一起使用,并且與re.ASCII不兼容。

在版本3.7中更改:使用re.LOCALE標(biāo)志編譯的正則表達(dá)式對象在編譯時(shí)不再依賴于語言環(huán)境。 匹配時(shí)只有語言環(huán)境會影響匹配結(jié)果。

re.M == re.MULTILINE

指定時(shí),模式字符'^'匹配字符串的開頭和每行的開頭(緊跟在每個(gè)換行符之后); 并且模式字符'$'匹配字符串的末尾和每行末尾(緊接在每個(gè)換行符之前)。 默認(rèn)情況下,'^'只匹配字符串的開頭,'$'只匹配字符串的末尾,緊接在字符串末尾的換行符(如果有的話)之前。 對應(yīng)于內(nèi)聯(lián)標(biāo)志(?m)。

re.S == re.DOTALL

制作'.' 特殊字符完全匹配任何字符,包括換行符; 沒有這個(gè)標(biāo)志,'.' 將匹配除換行符之外的任何內(nèi)容。 對應(yīng)于內(nèi)聯(lián)標(biāo)志(?s)。

re.X == re.VERBOSE

該標(biāo)志允許您通過一種編輯模式來編寫正則表達(dá)式,允許您在視覺上分離模式的邏輯部分并添加注釋,該正則表達(dá)式看起來更好,并且更易讀。模式中的空格被忽略,除非在字符類中,或者前面有一個(gè)未轉(zhuǎn)義的反斜杠,或者在諸如 *? , (?:(?P<...>)。我們還可以通過#號進(jìn)行注釋。

這意味著匹配一個(gè)十進(jìn)制數(shù)的下面兩個(gè)正則表達(dá)式對象在功能上是相等的:

    a = re.compile(r"""\d +  # the integral part
                       \.    # the decimal point
                       \d *  # some fractional digits""", re.X)
    b = re.compile(r"\d+\.\d*")

對應(yīng)于內(nèi)聯(lián)標(biāo)志(?x)。

Match Objects 匹配的結(jié)果對象

我們之前就提到過當(dāng)我們用寫好的規(guī)則去匹配一個(gè)字符串的時(shí)候,返回的不是匹配好的子字符串!而是返回一個(gè)匹配對象,里面除了包裝好子字符串,還提供其它的功能,因?yàn)?我們在寫規(guī)則的時(shí)候有通過捕獲括號對規(guī)則進(jìn)行分組,因此,我們也可以通過匹配對象把分組給提取出來。比如hello(\w) 規(guī)則能夠匹配 'helloA',如果直接返回'helloA',那我們設(shè)置的捕獲括號不就失去意義了嗎,而匹配對象則正是能夠完成這些功能的關(guān)鍵所在。

匹配對象始終具有布爾值True。 由于match()和search()在不匹配時(shí)返回None,因此可以通過簡單的if語句測試是否成功匹配:

match = re.search(pattern, string)
if match:
    process(match)

匹配對象支持以下方法和屬性:

match.expand(template)

我們設(shè)定一個(gè)模板,來顯示我們查找到的子字符串,其中,我們可以反斜杠替換的方式嵌入捕獲括號所匹配到的子字符串。
數(shù)字引用(\1, \2)或命名反斜線引用(\g<1>,\g <name>)被替換為相應(yīng)組的內(nèi)容。
比如我們要展示某個(gè)字符串中的數(shù)字,并列出個(gè)位數(shù)。

  import re
  p = re.compile(r'\d+(\d)(\d)')
  match = p.search("adsfaddf12345fgd")
  print(match .expand(r'The number in the string is \g<0>.\nThe last number is \2'))
  
  The number in the string is 12345.
  The last number is 5

match.group([group1, ...])

返回匹配的一個(gè)或多個(gè)子組。 如果只有一個(gè)參數(shù),結(jié)果是一個(gè)單獨(dú)的字符串; 如果有多個(gè)參數(shù),則結(jié)果是一個(gè)tuple。
沒有參數(shù),group默認(rèn)為零(整個(gè)匹配被返回)。 如果group參數(shù)為零,則相應(yīng)的返回值是整個(gè)匹配的字符串; 如果參數(shù)在包含范圍[1..99]中,則它是匹配相應(yīng)括號組的字符串。 如果組編號為負(fù)數(shù)或大于模式中定義的組數(shù),則會引發(fā)IndexError異常。 如果一個(gè)組包含在不匹配的模式的一部分中,則相應(yīng)的結(jié)果為無。 如果一個(gè)組包含在多次匹配的模式的一部分中,則返回最后的匹配。

    >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
    >>> m.group(0)       # 整個(gè)的匹配結(jié)果
    'Isaac Newton'
    >>> m.group(1)       # 第一個(gè)括號匹配到的結(jié)果
    'Isaac'
    >>> m.group(2)       # 第二個(gè)括號匹配到的結(jié)果
    'Newton'
    >>> m.group(1, 2)    # 返回多個(gè)結(jié)果
    ('Isaac', 'Newton')

如果正則表達(dá)式使用 (?P<name>...)語法,則group參數(shù)也可以是通過組名稱標(biāo)識組的字符串。 如果字符串參數(shù)未在模式中用作組名稱,則會引發(fā)IndexError異常。

    >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
    >>> m.group('first_name')
    'Malcolm'
    >>> m.group('last_name')
    'Reynolds'

命名組也可以通過它們的索引來引用:

    >>> m.group(1)
    'Malcolm'
    >>> m.group(2)
    'Reynolds'

如果一個(gè)組匹配多次,只能訪問最后一場匹配:

    >>> m = re.match(r"(..)+", "a1b2c3")  # 由于貪婪模式,這個(gè)表達(dá)式明顯匹配的結(jié)果是全部的字符串
     # 但是其中的括號匹配到的是什么呢?
    >>> m.group(1)                        # 返回最后能匹配到的
    'c3'

讀者可以自行試試這個(gè)例子,看看 m.group() m.group(0) m.group(1) m.group(2) 分別是什么

match.getitem(g)

如果你熟悉python就知道這個(gè)屬性就像dict一樣,讓我們能夠直接用[]的形式取出數(shù)據(jù),而不需要使用group函數(shù),更加方便快捷。
這與m.group(g)相同。 這允許從比賽中更容易地訪問個(gè)人組:

    >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
    >>> m[0]       # 等價(jià)于 m.group(0)
    'Isaac Newton'
    >>> m[1]       # 
    'Isaac'
    >>> m[2]       # 
    'Newton'

match.groups(default=None)

返回一個(gè)包含匹配所有子組的元組,從1開始,直到模式中有多個(gè)組。 default參數(shù)用于補(bǔ)齊沒能成功匹配的組; 它默認(rèn)為None。

例如:

    >>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
    >>> m.groups()
    ('24', '1632')

如果我們將小數(shù)點(diǎn)后的位置及其后的所有內(nèi)容都設(shè)為可選,則并非所有組都可以參與該匹配。 除非給出默認(rèn)參數(shù),否則這些組將默認(rèn)為None。

    >>> m = re.match(r"(\d+)\.?(\d+)?", "24")
    >>> m.groups()      # 第二組默認(rèn)為 None. 
    ('24', None)
    >>> m.groups('0')   # Now, the second group defaults to '0'.
    ('24', '0')

match.groupdict(default=None)

返回包含匹配的所有命名子組的字典的子集名稱。 缺省參數(shù)用于未參與匹配的組; 它默認(rèn)為None。 例如:

    >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
    >>> m.groupdict()
    {'first_name': 'Malcolm', 'last_name': 'Reynolds'}

match.start([group])

match.end([group])

返回按組匹配的子串在原字符串中開始和結(jié)束的位置;

組默認(rèn)為零,意味著整個(gè)匹配的子字符串,一個(gè)將從電子郵件地址中刪除remove_this的示例:

    >>> email = "tony@tiremove_thisger.net"
    >>> m = re.search("remove_this", email)
    >>> email[:m.start()] + email[m.end():]
    'tony@tiger.net'
    >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
    >>> m.start(0)   # 整個(gè)匹配到的是 "Isaac Newton" 其中"I"在原字符串的開頭,因此是 0
    0
    >>> m.end(0)   # 整個(gè)匹配到的是 "Isaac Newton" 其中"n"在原字符串的第12位,因此是 12
    12
    >>> m.start(1)  # 第一組匹配到的是 "Isaac" 其中"I"在原字符串的第0位,因此是 0
    0
    >>> m.end(1)  # 第一組匹配到的是 "Isaac" 其中"c"在原字符串的第5位,因此是 5
    5
    >>> m.start(2)  # 第2組匹配到的是 "Newton" 其中"N"在原字符串的第6位,因此是 6
    6
    >>> m.end(2)  # 第2組匹配到的是 "Newton" 其中"n"在原字符串的第12位,因此是 12
    12

match.span([group])

對于匹配m,返回2元組(m.start(group),m.end(group))。 請注意,如果組沒有參與匹配,則為(-1,-1)。 組默認(rèn)為零。對于上面的例子 m.span() 也就是 m.span(0) 返回 (0,12) m.span(1) 返回 (0,5) m.group(2) 返回 (6,12)

Match Object的一些參數(shù)

match.pos

傳遞給正則表達(dá)式對象的search()或match()方法的pos的值。 這是RE引擎開始尋找匹配的字符串的索引。根據(jù)此值,我們能夠知道在引擎搜索時(shí)設(shè)定的pos

match.endpos

傳遞給正則表達(dá)式對象的search()或match()方法的endpos的值。 這是RE引擎不會去的字符串的索引。

match.lastindex

最后一個(gè)匹配捕獲組的整數(shù)索引,或者如果沒有匹配組,則為None。 例如,如果將表達(dá)式(a)b,((a)(b))和((ab))應(yīng)用于字符串"ab",則lastindex == 1,而表達(dá)式(a)(b)將 如果應(yīng)用于相同的字符串,則lastindex == 2。

match.lastgroup

最后匹配的捕獲組的名稱,如果組沒有名稱,或者根本沒有匹配組,則為None。

match.re

正則表達(dá)式對象的match()或search()方法生成此匹配實(shí)例。

match.string

傳遞給match()或search()的字符串。

一些例子

尋找對子

在這個(gè)例子中,我們將使用以下輔助函數(shù)來更加優(yōu)雅地顯示匹配對象:

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

假設(shè)您正在編寫一個(gè)撲克程序,其中玩家的手牌為5個(gè)字符的字符串,每個(gè)字符代表一張牌,"a"代表王牌,"k"代表國王,"q"代表女王,"j"代表插孔, "t"為10,"2"至"9"代表具有該值的卡。

要查看給定的字符串是否是有效的,可以執(zhí)行以下操作:

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

最后一只手牌,"727ak",包含一對,或兩個(gè)相同的價(jià)值卡。 為了與正則表達(dá)式匹配,可以使用反斜線引用:

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

為了找出這對卡片組成的卡片,可以按照以下方式使用匹配對象的group()方法:

>>> pair.match("717ak").group(1)
'7'

# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'

>>> pair.match("354aa").group(1)
'a'

search() vs. match()

Python提供了基于正則表達(dá)式的兩種不同的基本操作:re.match()僅在字符串的開始處檢查匹配,而re.search()檢查字符串中任意位置的匹配(這是Perl默認(rèn)執(zhí)行的操作)。

例如:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<re.Match object; span=(2, 3), match='c'>

以'^'開頭的正則表達(dá)式可以與search()一起用于限制字符串開始處的匹配:

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<re.Match object; span=(0, 1), match='a'>

但是請注意,在MULTILINE模式下match()只匹配字符串的開頭,而使用search()與以'^'開頭的正則表達(dá)式匹配每行的開頭。

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<re.Match object; span=(4, 5), match='X'>

做一個(gè)電話本

split()將字符串分割成由正則表達(dá)式分隔的列表。該方法對于將文本數(shù)據(jù)轉(zhuǎn)換為可由Python輕松讀取和修改的數(shù)據(jù)結(jié)構(gòu)非常有用,如以下創(chuàng)建電話簿的示例所示。

首先,這是輸入。通常它可能來自一個(gè)文件:

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""

條目由一個(gè)或多個(gè)換行符分隔。 現(xiàn)在我們將字符串轉(zhuǎn)換為一個(gè)列表,每個(gè)非空行都有自己的條目:

>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']

最后,將每個(gè)條目分成一個(gè)名字,姓氏,電話號碼和地址。 因?yàn)榈刂分杏锌崭?為了不把地址分隔開,我們使用split()的maxsplit參數(shù),:

>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

我們可以將最多的房屋號碼與街道名稱分開:

>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]

Text Munging

Mung或munge是計(jì)算機(jī)術(shù)語,用于對一段數(shù)據(jù)或文件進(jìn)行一系列潛在的破壞性或不可撤銷的更改。它有時(shí)用于說話人尚不清楚的模糊數(shù)據(jù)轉(zhuǎn)換步驟。常見的搜索操作包括刪除標(biāo)點(diǎn)或html標(biāo)簽,數(shù)據(jù)解析,過濾和轉(zhuǎn)換。 [wiki]

sub()用一個(gè)字符串或一個(gè)函數(shù)的結(jié)果替換每個(gè)匹配規(guī)則的子部分。 這個(gè)例子演示了如何使用sub()和函數(shù)來"munge"文本,或者隨機(jī)化除了第一個(gè)和最后一個(gè)字符之外的每個(gè)單詞中所有字符的順序:

>>> def repl(m):
...     inner_word = list(m.group(2))
...     random.shuffle(inner_word)
...     return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'

尋找所有的動詞

findall()匹配所有匹配的子部分,而不僅僅是search()所做的第一個(gè)。 例如,如果一個(gè)人是作家,并且想要在某些文本中找到所有副詞,他或她可以按以下方式使用findall():

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']

尋找所有的動詞和對應(yīng)位置

如果想要獲得關(guān)于匹配文本的所有匹配的更多信息,finditer()很有用,因?yàn)樗峁┝似ヅ鋵ο蠖皇亲址?繼續(xù)前面的例子,如果一個(gè)作家想要在某些文本中找到所有副詞及其位置,他或她會按以下方式使用finditer():

>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
...     print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly

原始字符串符號

原始字符串符號(r"text")使正則表達(dá)式保持正常。 沒有它,正則表達(dá)式中的每個(gè)反斜杠( '' )必須以另一個(gè)反斜杠作為前綴。 例如,以下兩行代碼在功能上是相同的:

>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>

當(dāng)想要匹配文字反斜杠時(shí),它必須在正則表達(dá)式中轉(zhuǎn)義。 用原始字符串表示法,這意味著r"\"。 沒有原始字符串表示法,必須使用"\\",使以下代碼行功能相同:

>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>

總而言之,最好使用raw字符串

寫一個(gè)分詞器

分詞器或掃描器分析字符串以對字符組進(jìn)行分類。這是編寫編譯器或解釋器的第一步。所以,理論上來說,學(xué)完re模塊就可以寫一個(gè)屬于自己的計(jì)算機(jī)語言,努力吧,說不定你的語言會成為下一個(gè)python,成為廣受歡迎的語言。

下面就是告訴大家如何寫一個(gè)分詞器,讓你的編譯器識別代碼中的關(guān)鍵字,識別符,值等等元素。
編譯器首先需要把代碼中的一個(gè)個(gè)詞拆開分類為一個(gè)個(gè)token
一個(gè)token有'type' 屬性,'value'屬性,'line'屬性,'column'屬性
一條代碼 'if a > 1:' 就會被分為5個(gè)token
第一個(gè)token, 'type' 是關(guān)鍵字 , 'value'是IF, 'line'是該語句所在的行數(shù), 'column'是 if在該行從左往右數(shù)的位置
第二個(gè)token, 'type'是 標(biāo)識符(ID) 'value'是 a,
第三個(gè)token, 'type'是 操作符(Operator) 'value'是 '>'
第四個(gè)token, 'type'是 數(shù)量(Number) 'value'是 1
第五個(gè)token, 'type'是 判斷結(jié)束符(EndOfIf) 'value'是 :
就這樣把代碼拆開,準(zhǔn)確地識別各個(gè)token,是每一個(gè)編譯器工作的第一步。
token的結(jié)構(gòu)可以由你自己定義,但一般來說都要都有type屬性和value屬性,你還可以自己添加其他的屬性來方便編譯器工作,這取決于你的編程水平。

import collections
import re

# 這是一個(gè)簡單的定義一個(gè)class的方法,我們定義一個(gè)token類,我們把代碼拆開然后根據(jù)正則表達(dá)式的識別將其實(shí)例化為一個(gè)個(gè)token
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])

def tokenize(code):
    # 定義我們的編譯器有哪些關(guān)鍵字,不同的語言關(guān)鍵字是不一樣的,需要語言開發(fā)者自行定義
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}

    # 語言除了關(guān)鍵字,還有其他的類型
    token_specification = [
        
        # 定義我們語言能使用的數(shù)值
        ('NUMBER',  r'\d+(\.\d*)?'),  # Integer or decimal number
        # 定義語言的賦值符號
        ('ASSIGN',  r':='),           # Assignment operator
        #定義語句的結(jié)束標(biāo)識,一般來說都是分號,我們也用分號來標(biāo)識結(jié)尾吧
        ('END',     r';'),            # Statement terminator
        #標(biāo)識符,比如變量的名字,就是一個(gè)標(biāo)識符,我們這里規(guī)則變量只能用字母連下劃線都不能用
        ('ID',      r'[A-Za-z]+'),    # Identifiers
        #定義運(yùn)算符,我們的語言能進(jìn)行+-*/
        ('OP',      r'[+\-*/]'),      # Arithmetic operators
        #要能夠識別新的一行代碼
        ('NEWLINE', r'\n'),           # Line endings
        #要能夠識別各種空白
        ('SKIP',    r'[ \t]+'),       # Skip over spaces and tabs
        #其他任何的東西我們都是別錯(cuò)誤匹配,相當(dāng)于寫了錯(cuò)誤的語句,我們的語言要報(bào)錯(cuò),比如你在python里面寫prinf("hello")肯定是錯(cuò)誤的,這是C里面的語句
        ('MISMATCH',r'.'),            # Any other character
    ]

    #我們把上面的正則表達(dá)式用或串起來
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
    line_num = 1
    line_start = 0

    # 我們開始對代碼進(jìn)行識別
    for mo in re.finditer(tok_regex, code):
        kind = mo.lastgroup
        value = mo.group(kind)
        if kind == 'NEWLINE':
            line_start = mo.end()
            line_num += 1
        elif kind == 'SKIP':
            pass
        elif kind == 'MISMATCH':
            raise RuntimeError(f'{value!r} unexpected on line {line_num}')
        else:
            if kind == 'ID' and value in keywords:
                kind = value
            column = mo.start() - line_start
            yield Token(kind, value, line_num, column)

statements = '''
    IF quantity THEN
        total := total + price * quantity;
        tax := price * 0.05;
    ENDIF;
'''

# 把我們實(shí)例化的token打印出來
for token in tokenize(statements):
    print(token)

分詞器產(chǎn)生以下輸出:

Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value='0.05', line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF',line=5, column=4)
Token(type='END', value=';', line=5, column=9)

'

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內(nèi)容

  • Python中的正則表達(dá)式(re) import rere.match #從開始位置開始匹配,如果開頭沒有則無re...
    BigJeffWang閱讀 7,118評論 0 99
  • re模塊手冊 本模塊提供了和Perl里的正則表達(dá)式類似的功能,不關(guān)是正則表達(dá)式本身還是被搜索的字符串,都可以...
    喜歡吃栗子閱讀 4,026評論 0 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,776評論 18 139
  • #首先,python中的正則表達(dá)式大致分為以下幾部分: 元字符 模式 函數(shù) re 內(nèi)置對象用法 分組用法 環(huán)視用法...
    mapuboy閱讀 1,626評論 0 51
  • 昨晚這孩子得了急性中耳炎,整晚都在說我太困了好想睡覺啊,但是我太疼了!一大早六點(diǎn)就去了兒童醫(yī)院,中午剛吃點(diǎn)...
    光陰無蹤影閱讀 331評論 0 0