Python Regex入門(初級篇)_python

來源:互聯網
上載者:User

引子

首先說 Regex是什嗎?

Regex,又稱正規表示式、正規標記法、正規運算式、規則運算式、常規標記法(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),電腦科學的一個概念。Regex使用單個字串來描述、匹配一系列匹配某個句法規則的字串。在很多文字編輯器裡,Regex通常被用來檢索、替換那些匹配某個模式的文本。

許多程式設計語言都支援利用Regex進行字串操作。例如,在Perl中就內建了一個功能強大的Regex引擎。Regex這個概念最初是由Unix中的工具軟體(例如sed和grep)普及開的。Regex通常縮寫成“regex”,單數有regexp、regex,複數有regexps、regexes、regexen。

引用自維基百科https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F

定義是定義,太正經了就沒法用了。我們來舉個栗子:假如你在寫一個爬蟲,你得到了

一個網頁的HTML源碼。其中有一段

<html><body><h1>hello world<h1></body></html>

你想要把這個hello world提取出來,但你這時如果只會python 的字串處理,那麼第一反應可能是

s = <html><body><h1>hello world<h1></body></html>start_index = s.find('<h1>')

然後從這個位置向下尋找到下一個<h1>出現這樣做未嘗不可,但是很麻煩不是嗎。需要考慮多個標籤,一不留神就多匹配到東西了,而如果想要非常準確的匹配到,又得多加迴圈判斷,效率太低。

這時候,Regex就是首選的幫手。

乾貨開始

入門層級

接著說我們剛才那個例子。我們如果拿正則處理這個運算式要怎麼做呢?

import rekey = r"<html><body><h1>hello world<h1></body></html>"#這段是你要匹配的文本p1 = r"(?<=<h1>).+?(?=<h1>)"#這是我們寫的Regex規則,你現在可以不理解啥意思pattern1 = re.compile(p1)#我們在編譯這段Regexmatcher1 = re.search(pattern1,key)#在源文本中搜尋符合Regex的部分print matcher1.group(0)#列印出來

你可以嘗試運行上面的代碼,看看是不是和我們想象的一樣(博主是在python2.7環境下)發現代碼挺少挺簡單?往下看。而且Regex實際上要比看起來的那種奇形怪狀要簡單得多。

首先,從最基礎的Regex說起。

假設我們的想法是把一個字串中的所有"python"給匹配到。我們試一試怎麼做

import rekey = r"javapythonhtmlvhdl"#這是源文本p1 = r"python"#這是我們寫的Regexpattern1 = re.compile(p1)#同樣是編譯matcher1 = re.search(pattern1,key)#同樣是查詢print matcher1.group(0)

看完這段代碼,你是不是覺得:臥槽?這就是Regex?直接寫上去就行?

確實,Regex並不像它表面上那麼奇葩,如果不是我們故意改變一些符號的含義時,你看到的就是想要匹配的。

所以,先把大腦清空,先認為Regex就是和想要匹配的字串長得一樣。在之後的練習中我們會逐步進化

初級

0.無論是python還是Regex都是區分大小寫的,所以當你在上面那個例子上把"python"換成了"Python",那就匹配不到你心愛的python了。

1.重新回到第一個例子中那個<h1>hello world<h1>匹配。假如我像這麼寫,會怎麼樣?

import rekey = r"<h1>hello world<h1>"#源文本p1 = r"<h1>.+<h1>"#我們寫的Regex,下面會將為什麼pattern1 = re.compile(p1)print pattern1.findall(key)#發沒發現,我怎麼寫成findall了?咋變了呢?

有了入門級的經驗,我們知道那兩個<h1>就是普普通通的字元,但是中間的是什麼鬼?

.字元在Regex代表著可以代表任何一個字元(包括它本身)

findall返回的是所有符合要求的元素列表,包括僅有一個元素時,它還是給你返回的列表。

機智如你可能會突然問:那我如果就只是想匹配"."呢?結果啥都給我返回了咋整?在Regex中有一個字元\,其實如果你編程經驗較多的話,你就會發現這是好多地方的“轉義符”。在Regex裡,這個符號通常用來把特殊的符號轉成普通的,把普通的轉成特殊的23333(並不是特殊的“2333”,寫完才發現會不會有腦洞大的想歪了)。

舉個栗子,你真的想匹配"chuxiuhong@hit.edu.cn"這個郵箱(我的郵箱),你可以把Regex寫成下面這個樣子:

import rekey = r"afiouwehrfuichuxiuhong@hit.edu.cnaskdjhfiosueh"p1 = r"chuxiuhong@hit\.edu\.cn"pattern1 = re.compile(p1)print pattern1.findall(key)

發現了吧,我們在.的前面加上了轉義符\,但是並不是代表匹配“\.”的意思,而是只匹配“.”的意思!
不知道你細不細心,有沒有發現我們第一次用.時,後面還跟了一個+?那這個加號是幹什麼的呢?
其實不難想,我們說了“.字元在Regex代表著可以代表任何一個字元(包括它本身)”,但是"hello world"可不是一個字元啊。
+的作用是將前面一個字元或一個子運算式重複一遍或者多遍。
比方說運算式“ab+”那麼它能匹配到“abbbbb”,但是不能匹配到"a",它要求你必須得有個b,多了不限,少了不行。你如果問我有沒有那種“有沒有都行,有多少都行的表達方式”,回答是有的。
*跟在其他符號後面表達可以匹配到它0次或多次
比方說我們在王葉內遇到了連結,可能既有http://開頭的,又有https://開頭的,我們怎麼處理?

import rekey = r"http://www.nsfbuhwe.com and https://www.auhfisna.com"#胡編亂造的網址,別在意p1 = r"https*://"#看那個星號!pattern1 = re.compile(p1)print pattern1.findall(key)

輸出

['http://', 'https://']

2.比方說我們有這麼一個字串"cat hat mat qat",你會發現前面三個是實際的單詞,最後那個是我胡編亂造的(上百度查完是昆士蘭英語學院的縮寫= =)。如果你本來就知道"at"前面是c、h、m其中之一時這才構成單詞,你想把這樣的匹配出來。根據已經學到的知識是不是會想到寫出來三個Regex進行匹配?實際上不需要。因為有一種多字元匹方式

[]代表匹配裡面的字元中的任意一個

還是舉個栗子,我們發現啊,有的程式員比較過分,,在<html></html>這對標籤上,大小寫混用,老害得我們抓不到想要的東西,我們該怎麼應對?是寫16*16種Regex挨個匹配?no

import rekey = r"lalala<hTml>hello</Html>heiheihei"p1 = r"<[Hh][Tt][Mm][Ll]>.+?</[Hh][Tt][Mm][Ll]>"pattern1 = re.compile(p1)print pattern1.findall(key)

輸出

['<hTml>hello</Html>']

我們既然有了範圍性的匹配,自然有範圍性的排除。

[^]代表除了內部包含的字元以外都能匹配

還是cat,hat,mat,qat這個例子,我們想匹配除了qat以外的,那麼就應該這麼寫:

import rekey = r"mat cat hat pat"p1 = r"[^p]at"#這代表除了p以外都匹配pattern1 = re.compile(p1)print pattern1.findall(key)

輸出

為了方便我們寫簡潔的Regex,它本身還提供下面這樣的寫法

Regex 代表的匹配字元
[0-9] 0123456789任意之一
[a-z] 小寫字母任意之一
[A-Z] 大寫字母任意之一
\d 等同於[0-9]
\D 等同於[^0-9]匹配非數字
\w 等同於[a-z0-9A-Z_]匹配大小寫字母、數字和底線
\W 等同於[^a-z0-9A-Z_]等同於上一條取非

3.介紹到這裡,我們可能已經掌握了大致的Regex的構造方式,但是我們常常會在實戰中遇到一些匹配的不準確的問題。比方說:

import rekey = r"chuxiuhong@hit.edu.cn"p1 = r"@.+\."#我想匹配到@後面一直到“.”之間的,在這裡是hitpattern1 = re.compile(p1)print pattern1.findall(key)

輸出結果

['@hit.edu.']

呦呵!你咋能多了呢?我理想的結果是@hit.,你咋還給我加量了呢?這是因為Regex預設是“貪婪”的,我們之前講過,“+”代表是字元重複一次或多次。但是我們沒有細說這個多次到底是多少次。所以它會儘可能“貪婪”地多給我們匹配字元,在這個例子裡也就是匹配到最後一個“.”。

我們怎麼解決這種問題呢?只要在“+”後面加一個“?”就好了。

import rekey = r"chuxiuhong@hit.edu.cn"p1 = r"@.+?\."#我想匹配到@後面一直到“.”之間的,在這裡是hitpattern1 = re.compile(p1)print pattern1.findall(key)

輸出結果

['@hit.']

加了一個“?”我們就將貪婪的“+”改成了懶惰的“+”。這對於[abc]+,\w*之類的同樣適用。

小測驗:上面那個例子可以不使用懶惰匹配,想一種方法得到同樣的結果

**個人建議:在你使用"+","*"的時候,一定先想好到底是用貪婪型還是懶惰型,尤其是當你用到範圍較大的項目上時,因為很有可能它就多匹配字元回來給你!!!**

為了能夠準確的控制重複次數,Regex還提供

{a,b}(代表a<=匹配次數<=b)

還是舉個栗子,我們有sas,saas,saaas,我們想要sas和saas,我們怎麼處理呢?

import rekey = r"saas and sas and saaas"p1 = r"sa{1,2}s"pattern1 = re.compile(p1)print pattern1.findall(key)

輸出

['saas', 'sas']

如果你省略掉{1,2}中的2,那麼就代表至少匹配一次,那麼就等價於?

如果你省略掉{1,2}中的1,那麼就代表至多匹配2次。

下面列舉一些Regex裡的元字元及其作用

元字元 說明
. 代表任一字元
\
[ ] 匹配內部的任一字元或子運算式
[^] 對字元集和取非
- 定義一個區間
\ 對下一字元取非(通常是普通變特殊,特殊變普通)
* 匹配前面的字元或者子運算式0次或多次
*? 惰性匹配上一個
+ 匹配前一個字元或子運算式一次或多次
+? 惰性匹配上一個
? 匹配前一個字元或子運算式0次或1次重複
{n} 匹配前一個字元或子運算式
{m,n} 匹配前一個字元或子運算式至少m次至多n次
{n,} 匹配前一個字元或者子運算式至少n次
{n,}? 前一個的惰性匹配
^ 匹配字串的開頭
\A 匹配字串開頭
$ 匹配字串結束
[\b] 退格字元
\c 匹配一個控制字元
\d 匹配任一數字
\D 匹配數字以外的字元
\t 匹配製表符
\w 匹配任一數字字母底線
\W 不匹配數字字母底線

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的協助,如果有疑問大家可以留言交流,同時也希望多多支援雲棲社區!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.