Python的re模塊,正則表達式用法詳解,正則表達式中括號的用法


Pythonre模塊,正則表達式

#導入re模塊

import  re  

1match方法的使用:

result = re.match(正則表達式,待匹配的字符串)

正則表達式寫法

第一部分:

字符

功能

.(點,英文的句號)

匹配任意一個字符,除了\n

[]

匹配[]中列舉的字符

\d

匹配數字,即0-9

\D

匹配非數字

\s

匹配空白,即空格,tag

\S

匹配非空白

\w

匹配單詞字符,即數字,小寫字母,大寫字母,下划線_

\W

匹配非單詞字符

 

舉例:

>>> re.match(".","&and")  #.匹配任意字符,除了\n,只要第一個匹配,后面的and都是無所謂了,match方法就是這樣定義的。從字符串最左邊開始匹配,只要都匹配正則表達式,字符串后面的多余部分可以隨意

<_sre.SRE_Match object; span=(0, 1), match='&'>     #匹配到就有返回值,match是匹配到的具體內容,即 給一個.匹配了最左邊的第一個字符&

>>> re.match(".","\n")   #不匹配,就返回None

>>> re.match("[1234][a-z][A-Z]\d","1aZ9")   #正則表達式的意思是第一個字符要滿足在1-4范圍內,第二個字符是小寫字母a-z,第三個字符是大寫字符A-Z,第四個字符是數字,后面的字符串剛好滿足,所以返回了下面這串值。這里同樣的1aZ9匹配,1aZ9#@@%¥也是匹配的。

<_sre.SRE_Match object; span=(0, 4), match='1aZ9'>

>> re.match("\w\W","a%224")    #第一個匹配單詞字符,第二個匹配非單詞字符,后面的無所謂

<_sre.SRE_Match object; span=(0, 2), match='a%'>

>>> re.match("\w\W","ab")

第二部分,個數相關:

字符

功能含義

*

匹配一個字符出現0次或者多次,即可有可無

+

匹配一個字符出現1次或者多次,至少出現一次

一個字符出現1次或者0次,即要么出現1次要么不出現

{m}

匹配個數,前面的字符出現了m

{m,}

匹配個數,前面的字符至少出現了m

{m,n}

匹配個數,前面的字符出現了mn

 

舉例:

>>> re.match("\d*","1234aa")   #\d出現了0次或者多次

<_sre.SRE_Match object; span=(0, 4), match='1234'>  #匹配到的是1234

>>> re.match("\d*","abd")

<_sre.SRE_Match object; span=(0, 0), match=''>  #匹配到空字符

>>> re.match("\d+","abd")  #匹配不到,因為+意思是必須至少出現1

>>> re.match("\d{11}","18510666666")  #數字匹配11

<_sre.SRE_Match object; span=(0, 11), match='18510666666'>

# 匹配手機號,分析:手機號第一個數字必須為1,第二個數字應該是34589,第3個到第11個是任意數字,所以為:\d{9},表示9個數字,然后只能有11個數字,所以9個數字的后面加$表示匹配結束,只能匹配前面的11個數字
res = re.match(r"1[34589]\d{9}$", "13511111111")

>>> re.match("1[34589]\d{9}","12000000000")  #2個數字不滿足,不匹配

>>> re.match("1[34589]\d{9}","13000000000r")  #滿足,匹配

<_sre.SRE_Match object; span=(0, 11), match='13000000000'>

 

第三部分,邊界相關:

字符

功能含義

^

表示匹配一個字符的開頭

$

表示匹配一個字符的結尾

\b

匹配一個字符的邊界

\B

匹配非字符邊界

 

還是上面的例子,匹配手機號:

>>> re.match("1[34589]\d{9}","13000000000r")  #滿足,匹配

上面這個正則表達式是匹配的,但是顯然不對,手機號后面不應該有字母,怎么解決呢?在寫完的正則表達式后加$即可

>>> re.match("1[34589]\d{9}$","13000000000r")  #不滿足,不匹配

 

如果要匹配一個:D:\\python\\test,怎么辦?

>>> re.match("D:\\python\\test","D:\\python\\test")  #這樣寫是不對的,python會把\解析為轉義字符,如何能避免呢,下面這樣寫,在正則表達式的前面加r

>>> re.match(r"D:\\python\\test","D:\\python\\test")

<_sre.SRE_Match object; span=(0, 14), match='D:\\python\\test'>

Ps:推薦每當寫正則表達式的時候,不管后面有沒有轉義字符,都加r

 

re.match(r"^.+ve\B","ho ver")  #\B表示前面的字符e不在邊界

<_sre.SRE_Match object; span=(0, 5), match='ho ve'>

 

>>> re.match(r"^.+ve\b","ho  ve  r")

<_sre.SRE_Match object; span=(0, 5), match='ho ve'>

 

>>> re.match(r"\w+\b\s+\w+\b\s+\w+\b","I love python")

<_sre.SRE_Match object; span=(0, 13), match='I love python'>

 

 

|管道符,邏輯或符號,在正則表達式中一樣表示或,即匹配|符號兩邊的任意一個表達式都可以

比如,匹配0-100的數字:

#分析:第一個|符號前面的表達式是0,匹配0,第二個是100,第三個是匹配兩位數字或者1位數字,第一位數字是1-9,第2位數字是0-9,?意思是可以有可以沒有。

re.match(r"0|100|[1-9][\d?]$","90") 

<_sre.SRE_Match object; span=(0, 2), match='90'>

>>> re.match(r"0|100|[1-9][\d?]$","0")

<_sre.SRE_Match object; span=(0, 1), match='0'>

 

完善后:

>>> re.match(r"[1-9]?\d$|100","0")   #0匹配了第二個\d$

<_sre.SRE_Match object; span=(0, 1), match='0'>

 

第四部分,分組:

字符

功能含義

|

匹配左右任意一個表達式

ab

將括號中的字符作為一個分組

\num

引用分組num匹配到的字符串

(?P<name>)

給分組起名字

(?P=name)

引用分組的名字

 

舉例:

#定義一個字符串s

S=”<html><body><p>python</p></body></html>”

 

匹配上述的字符串,正則表達式如何寫?

分析:第一個<html>跟最后一個</html>是一對,內容是一樣的只是后面多了一個/,同理中間也是如此,這個時候可以使用分組的思想

先使用不分組的思想匹配:

res = r"<.+><.+><.+>.+</.+></.+></.+>"

>>> re.match(res,s)

<_sre.SRE_Match object; span=(0, 39), match='<html><body><p>python</p></body></html>'>

 

可以匹配到,然后使用分組思想,很簡單,就是將<>里面的內容用()包起來,然后后面使用索引:

>>> res = r"<(.+)><(.+)><(.+)>.+</\3></\2></\1>"

>>> re.match(res,s)

<_sre.SRE_Match object; span=(0, 39), match='<html><body><p>python</p></body></html>'>

分析:第一個(.+)是分組的第一個索引,匹配到的內容是html,你希望最后一個<>內也是這個值,那么就在最后的位置,寫\1表示匹配第一個分組的內容

有時分組內容太多,用索引會有可能亂掉,就可以使用起別名的方式,如下:

>>> res = r"<(?P<key1>.+)><(?P<key2>.+)><(?P<key3>.+)>.+</(?P=key3)></(?P=key2)></(?P=key1)>"

>>> re.match(res,s)

<_sre.SRE_Match object; span=(0, 39), match='<html><body><p>python</p></body></html>'>

原本第一個分組里面的內容是<(.+)>,想要起別名,就在.+的前面加上?P<key1>后面引用這個別名的時候:(?P=key1),要注意括號必須有,有括號才表示分組

 

如果想獲取<p></p>中間的內容,應該如何獲取呢,或者你想要獲取每一個分組的內容?

這時候可以使用group方法,接上面的:

 

>>> gr = re.match(res,s)

>>> gr.group()  #不寫索引值就默認把匹配到的所有值都打出來

'<html><body><p>python</p></body></html>'

>>> gr.group(1)  #獲取第一個分組

'html'

>>> gr.group(2) #獲取第2個分組

'body'

>>> gr.group(3) # 獲取第3個分組

'p'

>>> gr.group(4) #獲取第4個分組報錯了,因為沒有第4個了

Traceback (most recent call last):

  File "<input>", line 1, in <module>

IndexError: no such group

如果只想獲取<p></p>中間的內容python,那你要把中間匹配到的內容用()括起來,表示也是一個分組:

 

res = r"<(?P<key1>.+)><(?P<key2>.+)><(?P<key3>.+)>(.+)</(?P=key3)></(?P=key2)></(?P=key1)>"

gr = re.match(res,s)

gr.group(4)

'python'

第五部分,re模塊的其他方法:

Search方法,從待匹配的字符串中檢索出需要的,比如從閱讀次數為 999字符串中檢索出閱讀次數:

>>> ret = re.search(r"\d+","閱讀次數為:999")

>>> ret.group()

'999'

 

Search方法只能匹配到第1次的內容,比如:

>>> ret = re.search(r"\d+","閱讀次數為:999,瀏覽次數為:2000")

>>> ret.group()

'999'

如果想要把所有的數字都找出來,可以使用findall方法,findall方法返回的是一個列表,沒有group方法,可以直接打印列表

>>>ret = re.findall(r"\d+","閱讀次數為:999,瀏覽次數為:2000")

>>> ret

['999', '2000']

這里強調一下正則表達式中的()問題:

  1. 正則表達式中沒有括號時,就是正常匹配,跟之前講的match()里面的用法一樣。比如下面這段:
import re

str = 'wang 123 sun 456 a WANG'

pattern = re.compile(r'\w+\s+\w+')

li = pattern.findall(str)

print(li)

結果為:['wang 123', 'sun 456', 'a WANG']

  1. 如果有一個括號,輸出的內容就是括號中的內容,比如將正則表達式改為:
pattern = re.compile(r'(\w+)\s+\w+')

li = pattern.findall(str)

print(li)

輸出的結果為:['wang', 'sun', 'a']

匹配還是按照沒有括號的\w+\s+\w+這個正則表達式匹配,但是輸出的只有括號中的內容。

  1. 當正則表達式中有兩個括號時,那輸出list的每個元素是一個元組,每個元組包含兩個元素,這兩個元素對應的就是正則中兩個括號的內容。比如:
pattern = re.compile(r'((\w+)\s+\w+)')

li = pattern.findall(str)

print(li)

結果:[('wang 123', 'wang'), ('sun 456', 'sun'), ('a WANG', 'a')]

 

compile方法:

要提取出一個字符串中的英文字母,可以用以下的方法:

import re

pattern = re.compile('[a-zA-Z]')

s = pattern.findall('sdf223dgrz')

打印的結果為:['s', 'd', 'f', 'd', 'g', 'r', 'z']

compile方法的意思是編譯正則表達式模式,返回一個正則表達式對象,可以把常用的正則表達式通過compile編譯出來,方便以后的調用,比如想找出多個字符串中的英文字母,就可以像上面那樣寫,再多寫幾個findall方法。如果不用compile方法,上面那兩行代碼可以寫成:print(re.findall('[a-zA-Z]', 'sdf223dgrz'))

compile還有第2個參數是flags,默認傳0flags編譯標志位,用來修改正則表達式的匹配方式,常用的值有:

re.I  :匹配的時候忽略大小寫

re.L : 做本地化識別匹配

re.M : 多行匹配,影響^$

re.S : 使‘.’可以匹配到‘/n

 

比如找出下面str字符串中的wan開頭的,不區分大小寫:

str = 'wang xian sheng is a WANG'

pattern = re.compile(r'wan\w', re.I)


li = pattern.findall(str)

print(li)

打印結果為:['wang', 'WANG']

Sub方法可以替換匹配到的字符串

>>> ret = re.sub(r"\d+","1000","閱讀次數為:999 ")

>>> ret

'閱讀次數為:1000 '

sub方法的第一個參數是正則表達式,需要替換掉的內容,第2個參數為替換后的數據,第三個參數是待匹配待替換的字符串

第二個參數也可以為一個方法名,比如將閱讀次數加1

# 定義一個方法,有一個參數,接收匹配到的數據,並且返回一個數據

#sub方法會將匹配到的數據依次傳到方法read_num中,所以我們首先要使用group方法獲取到每個參數的具體內容,然后將這個字符串轉為int類型,再加1,注意return的時候要轉為字符串,這個位置的值只能是字符串

def read_num(num):
    num_temp = num.group()  
    num = int(num_temp) + 1
    return str(num)

s = "閱讀次數為:999,瀏覽次數為2000"
ret = re.sub(r"\d+",read_num,s)
print(ret)
#print結果:閱讀次數為:1000,瀏覽次數為2001

# 將句子中的pho換成python

>>> ret = re.sub(r"php","python","I love php")

>>> ret

'I love python'

區分大小寫:

>>> ret = re.sub(r"php","python","I love phP")

>>> ret

'I love phP'

 

Python中的貪婪和非貪婪

Python中默認的匹配時貪婪的(即盡可能多的匹配),那么如何能做到非貪婪呢?

“*”“”“+”“{m,n}”的后面加上?就可以做到非貪婪,盡可能少的匹配

比如:s="this is a number: 0312-265-8888",匹配出里面的電話號碼,如果用下面第一種方法:

>>> ret = re.match(r".+(\d+-\d+-\d+)",s)  #因為要匹配數字,所以把號碼用括號括起來

>>> ret.group()

'this is a number: 0312-265-8888'

#這個結果是這種方式提取到的號碼,明顯不是我們想要的

>>> ret.group(1)    

'2-265-8888'

 

這面這種方式就是貪婪方式,前面的.+盡可能的匹配多一些,后面的\d+只要有一個數字就可以滿足,所以別的數字都歸到了.+里面,在.+的后面加?后結果就不一樣了:

>>> ret = re.match(r".+?(\d+-\d+-\d+)",s)

>>> ret.group(1)

'0312-265-8888'

加了?改成了非貪婪模式,那就是說盡可能多的匹配后面的\d+

補充:

\s:匹配的是任意的空白字符,

\S:匹配的是任意的非空白字符

所以[\s\S]:就可以匹配到任意字符

[\s\S]*? 的意思是非貪婪模式下匹配任意字符,等同於.*? 再加一個參數re.S

 

這個的group()方法,還有另外的類似方法是groups(),舉例:

>>> ret = re.match(r"(.+?)(\d+-\d+-\d+)",s)

>>> ret.groups()

('this is a number: ', '0312-265-8888')

Groups方法返回一個元祖,每一個分組匹配到的字符串放到元祖里面。

上面的結果是加了?的,如果不加?,那么結果為:

>>> ret = re.match(r"(.+)(\d+-\d+-\d+)",s)

>>> ret.groups()

('this is a number: 031', '2-265-8888')

Split()方法的用法:

找出s中的單詞

>>> s = "hi python,hello world"

#用,或者空格分隔s,返回一個列表,注意:|的后面有一個空格

>>> ret = re.split(r",+| +",s)   

>>> ret

['hi', 'python', 'hello', 'world']

 

思考:

將多個http鏈接比如:https://www.cnblogs.com/sy_test/?1234的后半部分去掉,只留下https://www.cnblogs.com/sy_test/,如何做?

分析:后面這部分“?506458”不好匹配,但是前面比較有規律,思路就可以考慮匹配到前面的內容,即將所有的鏈接內容匹配到,利用分組將你想要的東西括號括起來,用分組里的內容替換整個內容。這個例子就是找到https://www.cnblogs.com/sy_test/然后去替換到https://www.cnblogs.com/sy_test/?1234

>>> ret = re.sub(r"(http://.+?/).*",lambda x:x.group(1),s)

>>> ret

'https://www.cnblogs.com/sy_test/'

 

解析:前面的正則表達式"(http://.+?/).*",可以匹配到的內容應該是整個http鏈接,把這個結果傳入到lambda函數中,x的值就是正則表達式匹配的結果,然后x.group(1)就是將分組的第一個返回。這樣就ok了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM