1 什么是正則表達式
正則表達式(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),又稱正規表示式、正規表示法、正規表達式、規則表達式、常規表示法,是計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列匹配某個句法規則的字符串。在很多文本編輯器里,正則表達式通常被用來檢索、替換那些匹配某個模式的文本。 --Wikipedia
簡單的來說,正則表達式就是一個字符串,代表了一定的格式,然后我們用這個字符串去判斷另外一個字符串中有沒有符合這個格式的字符串。例如有一串記錄了學生姓名學號等信息的字符串
student = “張三 2017612403”
現在想把這個字符串中學生的學號也就是“2017612403”這個字符串提取出來,已知學號都是10位的。使用最簡單的正則表達式怎么做呢?如下:
p1 = r’ 2017612403’
regular=re.compile(p1)
print(reg ular.findall(student))
是不是很簡單,前面說過,正則表達式表示的其實就是一個格式,然后程序用在另一個字符串中找到符合這個格式的字符串,上面這個程序中,第一句話設置正則表達式的字符串,這里是p1 = r’2017612403’。然后第二句話表示將字符串編譯成一個正則表達式(Pattern)對象,也就是把字符串轉換成其對應的格式,這里的格式就是表示從待匹配的字符串中尋找一個’ 2017612403’子字符串。第三句話就是執行正則表達式尋找待匹配字符串中滿足格式的子字符串了,如果匹配到,python中會返回所有匹配的子字符串列表。執行結果如下:
所以說,可以簡單地將正則表達式理解為表達了一定格式的字符串,程序通過這個格式,在待匹配的字符串中尋找滿足這個格式的子字符串,並返回所有找到的子字符串的列表(python)。
2 正則表達式常用規則
2.1 匹配任意字符
假設有一段字符串:
test = ‘gcc hello.c’
需要從中將hello.c匹配出來,可以使用正則表達式:
p1 = r’\s.+\.c’
執行結果:
現在解釋一下上面的正則表達式:r’\s.+\.c’。’\s’、’+’、’.’都是正則表達式中的元字符,正則表達式中常用的元字符定義為:
. |
代表任意字符 |
| |
邏輯或操作符 |
[ ] |
匹配內部的任一字符或子表達式 |
[^] |
對字符集和取非 |
- |
定義一個區間 |
\ |
對下一字符取非(通常是普通變特殊,特殊變普通) |
* |
匹配前面的字符或者子表達式0次或多次 |
*? |
惰性匹配上一個 |
+ |
匹配前一個字符或子表達式一次或多次 |
+? |
惰性匹配上一個 |
? |
匹配前一個字符或子表達式0次或1次重復 |
{n} |
匹配前一個字符或子表達式 |
{m,n} |
匹配前一個字符或子表達式至少m次至多n次 |
{n,} |
匹配前一個字符或者子表達式至少n次 |
{n,}? |
前一個的惰性匹配 |
^ |
匹配字符串的開頭 |
\A |
匹配字符串開頭 |
$ |
匹配字符串結束 |
[\b] |
退格字符 |
\c |
匹配一個控制字符 |
\d |
匹配任意數字 |
\D |
匹配數字以外的字符 |
\t |
匹配制表符 |
\w |
匹配任意數字字母下划線 |
\W |
不匹配數字字母下划線 |
從表中我們知道,’\s’表示匹配任意的空白字符一次,’.’表示匹配任意字符(換行符除外)一次,有關’+’的詳細規則將在2.2中介紹,現在只需要知道它的作用是重復匹配其前面的字符,比如這里,’+’前面的字符是’.’,就表示在這里可以有任意數量的字符,而且字符的內容也是任意的。於是上述正則表達式r’\s.+\.c’就可以知道其作用如下:
需要匹配的格式為:以一個空白字符開始,緊跟着任意個數的任意字符,並以’.c’格式結尾,也就是’ xxx.c’這種格式。
2.2 重復匹配的元字符
上面已經了解了什么是正則表達式,也了解了重復匹配元字符’+’的用法。現在看一下1中的正則表達式,會發現它只能匹配’ 2017612403’這一個字符串,如果需要提取一個班中所有人的學號,用上面的正則表達式的話,豈不是要將所有人的學號都列出來,這樣還需要程序干嘛?下面介紹正則表達式的一些基本規則,使用這些規則我們可以寫出靈活度很高的匹配規則。
還是上面的例子,如果一個班有60人,學生的信息都以”姓名 學號”的格式儲存在all_student這個列表中,需要提取所有人的學號信息,如果不用正則表達式,在python中可以使用下面的語句:
all_student_id = [student[-10:] for student in all_student]
這樣的問題是,如果student的格式稍微改變一下或添加了其他的信息,如寫成:
student = “張三 2017612403 22 電子信息工程”
那上面的語句就失效了,有沒有一種方法能准確的將字符串中十位數的學號提取出來呢?這里就需要使用正則表達式了。先看一下正則表達式里面提取十位數學號的辦法:
p1 = r’\d{10}?’
regular=re.compile(p1)
print(reg ular.findall(student))
這里的p1就是正則表達式的字符串,現在看不懂沒關系,只需要知道這個正則表達式表示的格式是一個十個數字的字符串,那么執行regular.findall(student)后,正則表達式就會再student這個字符串中尋找一個十個數字的字符串。執行結果:
現在來解釋一下上面的正則表達式。正則表達式的字符串為r’\d{10}?’,整個正則表達式可以分解為三部分:’\d’、’{10}’、’?’,這里的’\d’、’{}’、’?’也是正則表達式中的元字符。
’\d’表示需要匹配字符串中的任意一個數字,注意是一個,上面這個例子中,如果設置正則表達式為’\d’的執行結果是這樣的:
發現雖然數字都匹配出來了,但是每個數字都被分開了,如果需要匹配很多個數字怎么辦呢?--使用’+’或’*’這個元字符,’+’表示匹配一個或多個前一個字符,’*’表示匹配0個或多個前一個字符如果這樣用的話:r’\d+’就代表匹配一個或多個數字,看一下執行結果:
可以看見使用r’\d+’也將我們需要的學號提取出來了。但是從元字符’+’的定義中可以發現,元字符’+’只是表示匹配一個或多個其前一個字符,沒有指定匹配多少次,那么如果待匹配的字符串中有多個數字串的話,比如這樣的:
student = “張三 2017612403 22 電子信息工程”
那么正則表達式就會把我們不需要的字符也匹配出來了:
那么如何指定匹配字符的個數呢?這里就需要用到’{}’這個元字符了。有關’{}’的詳細介紹可以看上面的表格,’{n}’表示匹配n個前面的字符,已知學號是10位,那么可以用r’\d{10}’來從待匹配字符串中尋找10個連續的數字,也就是我們要提取的學號。
那么只剩最后一個問題了,一開始的正則表達式中的r’\d{10}?’中的’?’是干什么的呢?從’?’元字符的定義中,可以知道它的作用是惰性匹配又稱貪婪匹配。
2.3 惰性匹配與貪婪匹配
貪婪匹配就是從待匹配字符串中一次性盡可能多的匹配字符,惰性匹配就是從待匹配字符串中一次性盡可能少的匹配字符。
從2.1中可以知道,元字符’+’、’*’、’{}’可以從待匹配字符串中重復匹配某一個指定的字符,那么就存在這種情況:
test = ‘This is a <EM>first</EM> test’
需要從test中匹配兩次分別得到<EM>和</EM>,根據上面的知識,應該用正則表達式:r’<.+>’,但實際上這個表達式執行的結果是:
這是因為python中默認的匹配模式是貪婪匹配,當表達式匹配到一個’>’后,會再次嘗試從后面的字符串中匹配’>’,直到遇到換行符,所以這里會一直匹配’<EM>first</EM>’,而不是’<EM>’。為了改變這種行為,需要將匹配模式設置為惰性匹配模式,惰性匹配模式下,當表達式匹配到第一個’>’后,就會直接返回。通過元字符’?’可以將其前面的字符匹配模式設置為惰性匹配模式。
2.4 限定范圍的匹配
我們知道web通過<h1></h1>、<h2></h2>這種表示一級標題、二級標題等,如果現在需要提取一個web中三級標題到6級標題中的內容,使用上面的知識,很容易知道,可以使用下面的正則表達式來提取:
p1 = r’<h3>.+</h3>?’
p2 = r’<h4>.+</h4>?’
p3 = r’<h5>.+</h5>?’
這樣寫起來十分繁瑣,而且如果需要匹配的范圍是一個很大的范圍,就沒有辦法使用這種方式進行匹配,為了解決這個問題,就需要用到正則表達式范圍匹配的規則:’[]’。’[]’中括號中表示了當前位置字符需要滿足的取值范圍。假設有下面一段待匹配字符串:
test = r’css tss gss rss’
只需要將test中以字符’c、t、r’開頭的單詞提取出來,可以使用下面的正則表達式:
p1 = r’[ctr]ss’
執行結果:
‘[]’元字符中,在中括號里面的是當前位置上的字符的取值范圍,上面的例子表示’ss’前面的字母只能是’c’、’t’、’r’中的一個。
有了限定范圍的匹配,正則表達式的規則中,也有限定范圍內排除的規則,使用元字符’[^]’來表示,如上面的例子,如果正則表達式寫成:
p1 = r’[^ctr]ss’
就會把’ss’前面的字母除了’c’、’t’、’r’之外的單詞匹配出來。執行結果: