您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Python正則表達(dá)式是什么,怎么用”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
我們先介紹一下re模塊下的方法,這個是我們拿來就可以用的,當(dāng)然前提是知道一點正則表達(dá)式,如果對正則表達(dá)式完全不了解,可以先看一下后面正則表達(dá)式部分。
match方法從字符串的起始位置匹配一個模式,如果沒有匹配成功match就返回None
re.match(pattern, string, flags=0)
pattern:正則表達(dá)式 string:待匹配的字符串 flags:匹配模式(是否區(qū)分大小寫、單行匹配還是多行匹配)
match返回的是一個re.Match對象,后面會詳細(xì)介紹Match中的方法。
import re content = "Cats are smarter than dogs" # 第一個參數(shù)是正則表達(dá)式,re.I表示忽略大小寫 match = re.match(r'(cats)', content, re.I) print(type(match)) print(match.groups()) match = re.match(r'dogs', content, re.I) print(type(match)) # print(match.groups())
match主要是用于捕獲分組,所以盡量使用分組模式,不然匹配了也無法獲取結(jié)果,flag是re.I表示忽略大小寫。
另外非常重要的一點match只會找第一個匹配的分組:
import re content = "aa aa smarter aa dogs" match = re.match(r'(aa)', content, re.I) if match: print(match.groups())
上面輸出的是:('aa',)
掃描整個字符串并返回第一個成功的匹配,search和match不同之處在于,search沒有強制要求從開始匹配。
re.search(pattern, string, flags=0)
import re content = '+123abc456*def789ghi' # \w能夠匹配[a-zA-Z0-9_],+表示至少匹配一次 reg = r"\w+" match = re.search(reg, content) if match: print(match.group())
替換字符串中的匹配項
re.sub(pattern, repl, string, count=0, flags=0)
pattern: 正則表達(dá)式 repl: 替換的字符串,可以是函數(shù) string: 要被查找替換的字符串 count: 模式匹配后替換的最大次數(shù),默認(rèn)0表示替換所有的匹配,可選 flags: 可選參數(shù),匹配模式,默認(rèn)為0
替換和諧字符:
import re content = "do something fuck you" rs = re.sub(r'fuck', "*", content) print(rs)
非常簡單,把fuck替換為*
現(xiàn)在需求變了,我們需要被屏蔽的單詞有幾個字符,就替換為幾個*,怎么處理?
import re def calcWords(matched): num = len(matched.group()) return str(num * '*') content = "do something fuck you" rs = re.sub(r'fuck', calcWords, content) print(rs)
替換的字符串可以使用函數(shù),我們在函數(shù)中就可以非常容易的計算了
在字符串中找到正則表達(dá)式所匹配的所有子串,并返回一個列表,如果沒有找到匹配的,則返回空列表。
re.findall(pattern, string, flags=0)
pattern:正則表達(dá)式 string: 待匹配的字符串 flags: 可選參數(shù),匹配模式,默認(rèn)為0
import re content = '+123abc456*def789ghi' reg = r"\d+" rs = re.findall(reg, content) # ['123', '456', '789'] print(rs)
findall有一個讓人狂躁的地方是,如果正則表達(dá)式中有分組,就只會返回分組中的匹配。
import re content = '+123abc456*def789ghi' reg = r"\d+([a-z]+)" rs = re.findall(reg, content) # ['abc', 'ghi'] print(rs)
在字符串中找到正則表達(dá)式所匹配的所有子串,并把它們作為一個迭代器返回
re.finditer(pattern, string, flags=0)
pattern:正則表達(dá)式 string: 待匹配的字符串 flags: 可選參數(shù),匹配模式,默認(rèn)為0
import re content = '+123abc456*def789ghi' reg = r"\d+" rss = re.finditer(reg, content) # 123 456 789 for rs in rss: print(rs.group(), end=' ')
finditer和findall差不多,但是沒有findall的那個令人狂躁的如果有分組只返回分組的問題。
import re content = '+123abc456*def789ghi' reg = r"\d+([a-z]+)" rss = re.finditer(reg, content) # 123abc 789ghi for rs in rss: print(rs.group(), end=' ')
按照匹配的子串將字符串分割后返回列表
re.split(pattern, string, maxsplit=0, flags=0)
import re content = '+123abc456*def789ghi' reg = r"\d+" rs = re.split(reg, content) print(rs)
編譯正則表達(dá)式,生成一個正則表達(dá)式Pattern對象,前面的方法都會先調(diào)用這個方法得到一個Pattern對象,然后再使用Pattern對象的同名方法。
接下來,我們馬上就會介紹一下Pattern對象。
re.compile(pattern, flags=0)
Pattern對象是一個編譯好的正則表達(dá)式,Pattern不能直接實例化,必須使用re.compile()進(jìn)行構(gòu)造。
屬性 | 說明 |
---|---|
pattern | 編譯使用的正則表達(dá)式 |
flags | 編譯時用的匹配模式,數(shù)字形式 |
groups | 表達(dá)式中分組的數(shù)量 |
groupindex | 字典,鍵是分組的別名,值是分組的編號 |
import re pattern = re.compile(r'(\w+)(?P<gname>.*)', re.S) # pattern: (\w+)(?P<gname>.*) print("pattern:", pattern.pattern) # flags: 48 print("flags:", pattern.flags) # groups: 2 print("groups:", pattern.groups) # groupindex: {'gname': 2} print("groupindex:", pattern.groupindex)
前面re模塊中介紹的方法對于Pattern都適用,只是少了pattern參數(shù)
其實很簡單,re模塊中的方法使用pattern參數(shù),通過re.compile方法構(gòu)造了一個Pattern對象
Match對象是一次匹配的結(jié)果,包含此次匹配的信息,可以使用Match提供的屬性或方法來獲取這些信息。
屬性 | 說明 |
---|---|
string | 匹配時使用的文本 |
re | 獲取Pattern的表達(dá)式 |
pos | 文本中正則表達(dá)式開始搜索的位置 |
endpos | 文本中正則表達(dá)式結(jié)束搜索的位置 |
lastindex | 最后一個被捕獲的分組在文本中的索引。如果沒有被捕獲的分組,將為None |
lastgroup | 最后一個被捕獲的分組的別名。如果沒有被捕獲的分組,將為None |
import re content = "123456<H1>first</h2>123456" reg = r'\d+<[hH](?P<num>[1-6])>.*?</[hH](?P=num)>' match = re.match(reg, content) # string: 123456<H1>first</h2>123456 print("string:", match.string) # re: re.compile('\\d+<[hH](?P<num>[1-6])>.*?</[hH](?P=num)>') print("re:", match.re) # pos: 0 print("pos:", match.pos) # endpos: 26 print("endpos:", match.endpos) # lastindex: 1 print("lastindex:", match.lastindex) # lastgroup: num print("lastgroup:", match.lastgroup)
感覺Match的屬性稍微有些雞肋。
方法 | 說明 |
---|---|
groups() | 獲取所有分組匹配的字符串,返回元組 |
group([group1,……]) | 獲取分組匹配到的字符串,返回元組 |
start(group) | 獲取分組在原始字符串中的開始匹配位置 |
end(group) | 獲取分組在原始字符串中的結(jié)束匹配位置 |
span(group) | 獲取分組在原始字符串中的開始和結(jié)束匹配位置,元組 |
groupdict() | 獲取有別名的分組匹配的字符串,返回字典,別名是鍵 |
expand(template) | template字符串中可以通過別名和編號引用匹配字符串 |
注意:無參數(shù)group等價于group(0),返回整個匹配到的字符串
import re match = re.match(r'(\w+) (\w+) (?P<name>.*)', 'You love sun') # groups(): ('You', 'love', 'sun') print("groups():", match.groups()) # group(2,3): ('love', 'sun') print("group(2,3):", match.group(2, 3)) # start(2): 4 print("start(2):", match.start(2)) # end(2): 8 print("end(2):", match.end(2)) # span(2): (4, 8) print("span(2):", match.span(2)) # groupdict(): {'name': 'sun'} print("groupdict():", match.groupdict()) # expand(r'I \2 \1!'): I love You! print(r"expand(r'I \2 \1!'):", match.expand(r'I \2 \1!'))
上面Match中的方法還是比較重要的,因為我們基本最后都是通過Match對象中的方法來獲取匹配的
表達(dá)式 | 說明 |
---|---|
. | 匹配任意字符,除了換行符,當(dāng)re.S標(biāo)記被指定時,則可以匹配包括換行符的任意字符 |
? | 匹配0個或1個由前面的正則表達(dá)式定義的片段,非貪婪方式 |
+ | 匹配1個或多個的表達(dá)式 |
* | 匹配0個或多個的表達(dá)式 |
[] | 用來表示一組字符,單獨列出,[abc] 匹配 'a'、'b'或'c' |
[^] | 不在[]中的字符,[^abc] 匹配除了a,b,c之外的字符 |
^ | 匹配字符串開頭,多行模式也匹配行結(jié)尾 |
\A | 匹配字符串開頭 |
$ | 匹配字符串末尾,多行模式也匹配行結(jié)尾 |
\Z | 匹配字符串末尾 |
{n} | 精確n個,"o{2}"匹配food,不匹配fod、foood |
{n,} | 至少n個,"o{2,}"匹配food,foood,不匹配fod |
{n, m} | 匹配n到m,"o{2,3}",匹配food,foood,不匹配fod、fooood |
| | a|b,匹配a或b |
- | -可以表示區(qū)間,[0-9]表示可以匹配0-9中的任意數(shù)字 |
最常用的就是.匹配任意字符,a.b就可以匹配abb、acb、adb、a+b、a8b等等
?表示最多匹配一次:abb?可以匹配ab、abb,但是不能匹配abbabb,因為?只是指前一個片段
+表示至少匹配一次:abb+可以匹配abb、abbb、abbbb等等,當(dāng)時不能匹配ab
*表示0到多次: abb*可以匹配ab、abb、abbb、abbbb等等
[]中有一組字符,字符間的關(guān)系是或
表達(dá)式 | 說明 |
---|---|
\t | 制表符 |
\n | 換行 |
\r | 回車 |
\f | 換頁 |
\w | 匹配數(shù)字、字母、下劃線,等價于[a-zA-Z0-9_] |
\W | 匹配非(數(shù)字、字母、下劃線),等價于[^a-zA-Z0-9_] |
\s | 匹配空白字符,等價于[\t\n\r\f] |
\S | 匹配非空字符,等價于[^\t\n\r\f] |
\d | 匹配數(shù)字,等價于[0-9] |
\D | 匹配非數(shù)字,等價于[^0-9] |
\b | 匹配單詞邊界,'er\b' 可以匹配"over"中的'er',但不能匹配"service"中的'er' |
\B | 匹配非單詞邊界,'er\B'能匹配"service"中的'er',但不能匹配"over"中的'er' |
表達(dá)式 | 說明 |
---|---|
(re) | 分組匹配,嵌套模式分組計數(shù)是從左到右,從外到里 |
\number | 引用分組,使用\1、\2、\3……訪問第1、2、3……分組 |
(?P<name>) | 指定分組名稱,使用name做為分組的別名 |
(?P=name) | 引用分組名稱,通過name應(yīng)用分組 |
分組最重要的一個作用就是可以回溯,就是引用已經(jīng)匹配的模式。
思考:html中如何匹配全部h標(biāo)簽?
reg = '<[hH][1-6]>.*?</[hH][1-6]>'
很多朋友可能會寫出類似于上面的表達(dá)式,有什么問題嗎?
看下面的例子:
import re content = ''' <html> <body> <H1>first</h2> <p>p tag</p> <h3>h3</h3> <h4>非法標(biāo)簽</h5> </body> </html> ''' rs = re.findall(r'<[hH][1-6]>.*?</[hH][1-6]>', content) print(rs) rs = re.findall(r'<[hH]([1-6])>.*?</[hH]\1>', content) print(rs) rs = re.findall(r'((<[hH]([1-6])>).*?</[hH]\3>)', content) print(rs) rs = re.findall(r'((<[hH](?P<num>[1-6])>).*?</[hH](?P=num)>)', content) print(rs)
看輸出,我們知道:
reg = '<[hH][1-6]>.*?</[hH][1-6]>'
會把'<h4>非法標(biāo)簽</h5>'這一部分也匹配到。
我們可以通過分組,然后引用分組來解決這個問題。
reg1 = '<[hH]([1-6])>.*?</[hH]\1>' reg2 = '((<[hH]([1-6])>).*?</[hH]\3>)'
因為如果有分組,findall之后打印出匹配分組,所以我們使用reg2這個正則表達(dá)式。
為什么是\3呢?
因為根據(jù)從左到右,從外到里的原則,我們知道([1-6])是第3個分組。
如果覺得不想去數(shù),或者怕數(shù)錯,那么就可以使用別名的方式。
reg = '((<[hH](?P<num>[1-6])>).*?</[hH](?P=num)>)'
表達(dá)式 | 說明 |
---|---|
(?=re) | 前向匹配,a(?=\d),匹配數(shù)字前面的a |
(?!re) | 前向反向匹配,a(?!\d),匹配后面不接數(shù)字的a |
(?<=re) | 向后匹配,(?<=\d)a,匹配前面是數(shù)字的a |
(?< !re) | 向后反向匹配,(?< !\d)a,匹配前面不是數(shù)字的a |
前后匹配也是一個非要有用的功能,它的一個重要特性是不消費re部分,下面看一個例子幫助理解。
import re content = ''' http://www.mycollege.vip https://mail.mycollege.vip ftp://ftp.mycollege.vip ''' # 向前匹配,匹配:前面的schema,不消費: rs = re.findall(r'.+(?=:)', content) # ['http', 'https', 'ftp'] print(rs) # 向后匹配,匹配//后面的域名,不消費// rs = re.findall(r'(?<=//).+', content) # ['www.mycollege.vip', 'mail.mycollege.vip', 'ftp.mycollege.vip'] print(rs) # 向后匹配,匹配$后面的數(shù)字,不消費$ price = ''' item1:$99.9 CX99:$199 ZZ88:$999 ''' rs = re.findall(r'(?<=\$)[0-9.]+', price) # ['99.9', '199', '999'] print(rs) # 前后匹配 title = ''' <head> <title>this is title</title> </head> ''' rs = re.findall(r'(?<=<title>).*(?=</title>)', title) # ['this is title'] print(rs)
表達(dá)式 | 說明 |
---|---|
(?:re) | (re)的不分組版本,(?:abc){2},匹配abcabc |
(?imsuxL:re) | 在括號中使用imsuxL對應(yīng)flag中的大寫,(?i:abc),匹配ABC |
(?-imsuxL:re) | 在括號中不使用imsuxL對應(yīng)flag中的大寫 |
(?#...) | #后面的內(nèi)容為注釋忽略 |
模式 | 說明 |
---|---|
re.I | IGNORECASE,使匹配對大小寫不敏感 |
re.M | MULTILINE,多行匹配,影響^和$ |
re.S | DOTALL,點任意匹配模式,使.匹配包括換行在內(nèi)的所有字符 |
re.X | VERBOSE,詳細(xì)模式,忽略表達(dá)式中的空白和注釋,并允許使用#添加注釋 |
re.L | LOCALE |
re.U | UNICODE |
re.M是多行匹配模式:
^可以匹配字符串開頭,也可以匹配行的開頭,字符串中換行符\n之后的位置
$可以匹配字符串結(jié)尾,也可以匹配行的結(jié)尾,字符串中換行符\n之前的位置
單行模式:
^等價于\A
$等價于\Z
import re content = ''' first line second line third line ''' # ['first', 'second', 'third'] rs = re.findall(r'^(.*) line$', content, re.M) # [] # rs = re.findall(r'^(.*) line$', content) # [] # rs = re.findall(r'\A(.*) line\Z', content, re.M) print(rs)
上面的小例子多行模式可以匹配成功,單行模式不能,因為單行模式^不能匹配\n后的位置,所以開頭就不匹配。
反過來,我們在決定是否使用re.M的時候,只需要考慮正則表達(dá)式中有沒有^和$,如果沒有肯定是不需要的,如果有,那么思考是否需要匹配\n(換行符)前后的位置,需要則加上re.M。
re.L和re.U比較不好理解,2.x和3.x的變化也很大,基本用不上,有興趣可以看一下文檔。
“Python正則表達(dá)式是什么,怎么用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。