引子
首先說 正則表達式是什么?
正則表達式,又稱正規表示式、正規表示法、正規表達式、規則表達式、常規表示法(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列匹配某個句法規則的字符串。在很多文本編輯器里,正則表達式通常被用來檢索、替換那些匹配某個模式的文本。
許多程序設計語言都支持利用正則表達式進行字符串操作。例如,在Perl中就內建了一個功能強大的正則表達式引擎。正則表達式這個概念最初是由Unix中的工具軟件(例如sed和grep)普及開的。正則表達式通??s寫成“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 的字符串處理,那么第一反應可能是
1
2
|
s = <html><body><h1>hello world<h1>< / body>< / html> start_index = s.find( '<h1>' ) |
然后從這個位置向下查找到下一個<h1>出現這樣做未嘗不可,但是很麻煩不是嗎。需要考慮多個標簽,一不留神就多匹配到東西了,而如果想要非常準確的匹配到,又得多加循環判斷,效率太低。
這時候,正則表達式就是首選的幫手。
干貨開始
入門級別
接著說我們剛才那個例子。我們如果拿正則處理這個表達式要怎么做呢?
1
2
3
4
5
6
|
import re key = r "<html><body><h1>hello world<h1></body></html>" #這段是你要匹配的文本 p1 = r "(?<=<h1>).+?(?=<h1>)" #這是我們寫的正則表達式規則,你現在可以不理解啥意思 pattern1 = re. compile (p1) #我們在編譯這段正則表達式 matcher1 = re.search(pattern1,key) #在源文本中搜索符合正則表達式的部分 print matcher1.group( 0 ) #打印出來 |
你可以嘗試運行上面的代碼,看看是不是和我們想象的一樣(博主是在python2.7環境下)發現代碼挺少挺簡單?往下看。而且正則表達式實際上要比看起來的那種奇形怪狀要簡單得多。
首先,從最基礎的正則表達式說起。
假設我們的想法是把一個字符串中的所有"python"給匹配到。我們試一試怎么做
1
2
3
4
5
6
|
import re key = r "javapythonhtmlvhdl" #這是源文本 p1 = r "python" #這是我們寫的正則表達式 pattern1 = re. compile (p1) #同樣是編譯 matcher1 = re.search(pattern1,key) #同樣是查詢 print matcher1.group( 0 ) |
看完這段代碼,你是不是覺得:臥槽?這就是正則表達式?直接寫上去就行?
確實,正則表達式并不像它表面上那么奇葩,如果不是我們故意改變一些符號的含義時,你看到的就是想要匹配的。
所以,先把大腦清空,先認為正則表達式就是和想要匹配的字符串長得一樣。在之后的練習中我們會逐步進化
初級
0.無論是python還是正則表達式都是區分大小寫的,所以當你在上面那個例子上把"python"換成了"Python",那就匹配不到你心愛的python了。
1.重新回到第一個例子中那個<h1>hello world<h1>
匹配。假如我像這么寫,會怎么樣?
1
2
3
4
5
|
import re key = r "<h1>hello world<h1>" #源文本 p1 = r "<h1>.+<h1>" #我們寫的正則表達式,下面會將為什么 pattern1 = re. compile (p1) print pattern1.findall(key) #發沒發現,我怎么寫成findall了?咋變了呢? |
有了入門級的經驗,我們知道那兩個<h1>
就是普普通通的字符,但是中間的是什么鬼?
.
字符在正則表達式代表著可以代表任何一個字符(包括它本身)
findall返回的是所有符合要求的元素列表,包括僅有一個元素時,它還是給你返回的列表。
機智如你可能會突然問:那我如果就只是想匹配"."呢?結果啥都給我返回了咋整?在正則表達式中有一個字符\,其實如果你編程經驗較多的話,你就會發現這是好多地方的“轉義符”。在正則表達式里,這個符號通常用來把特殊的符號轉成普通的,把普通的轉成特殊的23333(并不是特殊的“2333”,寫完才發現會不會有腦洞大的想歪了)。
舉個栗子,你真的想匹配"[email protected]"這個郵箱(我的郵箱),你可以把正則表達式寫成下面這個樣子:
1
2
3
4
5
|
import re p1 = r "chuxiuhong@hit\.edu\.cn" pattern1 = re. compile (p1) print pattern1.findall(key) |
發現了吧,我們在.
的前面加上了轉義符\
,但是并不是代表匹配“\.”的意思,而是只匹配“.”的意思!
不知道你細不細心,有沒有發現我們第一次用.
時,后面還跟了一個+
?那這個加號是干什么的呢?
其實不難想,我們說了“.
字符在正則表達式代表著可以代表任何一個字符(包括它本身)”,但是"hello world"可不是一個字符啊。
+的作用是將前面一個字符或一個子表達式重復一遍或者多遍。
比方說表達式“ab+”那么它能匹配到“abbbbb”,但是不能匹配到"a",它要求你必須得有個b,多了不限,少了不行。你如果問我有沒有那種“有沒有都行,有多少都行的表達方式”,回答是有的。
*跟在其他符號后面表達可以匹配到它0次或多次
比方說我們在王葉內遇到了鏈接,可能既有http://開頭的,又有https://開頭的,我們怎么處理?
1
2
3
4
5
|
import re key = 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其中之一時這才構成單詞,你想把這樣的匹配出來。根據已經學到的知識是不是會想到寫出來三個正則表達式進行匹配?實際上不需要。因為有一種多字符匹方式
[]
代表匹配里面的字符中的任意一個
還是舉個栗子,我們發現啊,有的程序員比較過分,,在<html></html>
這對標簽上,大小寫混用,老害得我們抓不到想要的東西,我們該怎么應對?是寫16*16種正則表達式挨個匹配?no
1
2
3
4
5
|
import re key = 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以外的,那么就應該這么寫:
1
2
3
4
5
|
import re key = r "mat cat hat pat" p1 = r "[^p]at" #這代表除了p以外都匹配 pattern1 = re. compile (p1) print pattern1.findall(key) |
輸出
為了方便我們寫簡潔的正則表達式,它本身還提供下面這樣的寫法
正則表達式 | 代表的匹配字符 |
---|---|
[0-9] | 0123456789任意之一 |
[a-z] | 小寫字母任意之一 |
[A-Z] | 大寫字母任意之一 |
\d | 等同于[0-9] |
\D | 等同于[^0-9]匹配非數字 |
\w | 等同于[a-z0-9A-Z_]匹配大小寫字母、數字和下劃線 |
\W | 等同于[^a-z0-9A-Z_]等同于上一條取非 |
3.介紹到這里,我們可能已經掌握了大致的正則表達式的構造方式,但是我們常常會在實戰中遇到一些匹配的不準確的問題。比方說:
1
2
3
4
5
|
import re p1 = r "@.+\." #我想匹配到@后面一直到“.”之間的,在這里是hit pattern1 = re. compile (p1) print pattern1.findall(key) |
輸出結果
['@hit.edu.']
呦呵!你咋能多了呢?我理想的結果是@hit.,你咋還給我加量了呢?這是因為正則表達式默認是“貪婪”的,我們之前講過,“+”代表是字符重復一次或多次。但是我們沒有細說這個多次到底是多少次。所以它會盡可能“貪婪”地多給我們匹配字符,在這個例子里也就是匹配到最后一個“.”。
我們怎么解決這種問題呢?只要在“+”后面加一個“?”就好了。
1
2
3
4
5
|
import re p1 = r "@.+?\." #我想匹配到@后面一直到“.”之間的,在這里是hit pattern1 = re. compile (p1) print pattern1.findall(key) |
輸出結果
['@hit.']
加了一個“?”我們就將貪婪的“+”改成了懶惰的“+”。這對于[abc]+,\w*之類的同樣適用。
小測驗:上面那個例子可以不使用懶惰匹配,想一種方法得到同樣的結果
**個人建議:在你使用"+","*"的時候,一定先想好到底是用貪婪型還是懶惰型,尤其是當你用到范圍較大的項目上時,因為很有可能它就多匹配字符回來給你?。?!**
為了能夠準確的控制重復次數,正則表達式還提供
{a,b}(代表a<=匹配次數<=b)
還是舉個栗子,我們有sas,saas,saaas,我們想要sas和saas,我們怎么處理呢?
1
2
3
4
5
|
import re key = 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次。
下面列舉一些正則表達式里的元字符及其作用
元字符 | 說明 |
---|---|
. | 代表任意字符 |
\ | |
[ ] | 匹配內部的任一字符或子表達式 |
[^] | 對字符集和取非 |
- | 定義一個區間 |
\ | 對下一字符取非(通常是普通變特殊,特殊變普通) |
* | 匹配前面的字符或者子表達式0次或多次 |
*? | 惰性匹配上一個 |
+ | 匹配前一個字符或子表達式一次或多次 |
+? | 惰性匹配上一個 |
? | 匹配前一個字符或子表達式0次或1次重復 |
{n} | 匹配前一個字符或子表達式 |
{m,n} | 匹配前一個字符或子表達式至少m次至多n次 |
{n,} | 匹配前一個字符或者子表達式至少n次 |
{n,}? | 前一個的惰性匹配 |
^ | 匹配字符串的開頭 |
\A | 匹配字符串開頭 |
$ | 匹配字符串結束 |
[\b] | 退格字符 |
\c | 匹配一個控制字符 |
\d | 匹配任意數字 |
\D | 匹配數字以外的字符 |
\t | 匹配制表符 |
\w | 匹配任意數字字母下劃線 |
\W | 不匹配數字字母下劃線 |
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,同時也希望多多支持服務器之家!
原文鏈接:http://www.cnblogs.com/chuxiuhong/p/5885073.html