一、正則表達(dá)式
re模塊是python獨(dú)有的匹配字符串的模塊,該模塊中提供的很多功能是基于正則表達(dá)式實現(xiàn)的,
而正則表達(dá)式是對字符串進(jìn)行模糊匹配,提取自己需要的字符串部分,他對所有的語言都通用。
- re模塊是python獨(dú)有的
- 正則表達(dá)式所有編程語言都可以使用
- re模塊、正則表達(dá)式是對字符串進(jìn)行操作
(一)常用正則
1、字符
| 元字符 | 匹配內(nèi)容 |
|---|---|
| . | 匹配除換行符以外的任意字符 |
| \n | 匹配一個換行符 |
| \t | 匹配一個制表符 |
| ^ | 匹配字符串的開始 |
| $ | 匹配字符串的結(jié)尾 |
| a|b | 匹配字符a或字符b |
| [...] | 匹配字符組中的字符 |
| [^...] | 匹配除了字符組中字符的所有字符 |
| () | 匹配括號內(nèi)的表達(dá)式,也表示一個組 |
| (?:表達(dá)式) | 取消括號的分組功能 |
2、字符集
| 正則 | 說明 |
|---|---|
| \d | 匹配數(shù)字,它相當(dāng)于類[0-9] |
| \D | 匹配非數(shù)字,它相當(dāng)于類[^0-9] |
| \s | 匹配任意的空白符,它相當(dāng)于類[\t\n\r\f\v] |
| \S | 匹配非空白符,它相當(dāng)于類[^\t\n\r\f\v] |
| \w | 匹配字母或數(shù)字或下劃線,它相當(dāng)于類[a-zA-Z0-9_] |
| \W | 匹配非字母或數(shù)字或下劃線,它相當(dāng)于類[^a-zA-Z0-9_] |
| \b | 匹配位于開始或結(jié)尾的空字符串 |
| \B | 匹配不位于開始或結(jié)尾的空字符串,它相當(dāng)于類[^\b] |
| \A | 僅匹配字符串開頭,同^ |
| \Z | 僅匹配字符串結(jié)尾,同$ |
3、量詞
貪婪模式:總是嘗試匹配盡可能多的字符
非貪婪則相反,總是嘗試匹配盡可能少的字符。
| 量詞 | 用法說明 |
|---|---|
| * | 重復(fù)零次或更多次 |
| + | 重復(fù)一次或更多次 |
| ? | 重復(fù)零次或一次 |
| {n} | 重復(fù)n次 |
| {n,} | 重復(fù)n次至無限次 |
| {n,m} | 重復(fù)n到m次 |
{0,}匹配前一個字符0或多次,等同于*元字符
{+,}匹配前一個字符1次或無限次,等同于+元字符
{0,1}匹配前一個字符0次或1次,等同于?元字符
如果()后面跟的是特殊元字符如 (adc)* 那么*控制的前導(dǎo)字符就是()里的整體內(nèi)容,不再是前導(dǎo)一個字符
4、特殊分組用法表:只對正則函數(shù)返回對象的有用
| 用法 | 用法說明 |
|---|---|
| (?P<name>) | 分組起別名,?P<>定義組里匹配內(nèi)容的key(鍵),<>里面寫key名稱,值就是匹配到的內(nèi)容,用groupdict()打印字符串 |
| (?P=name) | 引用別名為<name>的分組匹配到字符串 |
| \num | 引用編號為num的分組匹配到字符串,第0組是整體 |
import re
#匹配出“<html><h1>[www.it123.com](http://www.it123.com/)</h1></html>”
ret = re.search(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.it123.com</h1></html>")
print(ret.group())
#<html><h1>www.it123.com</h1></html>
#先給兩個分組起了別名name1、name2,后邊引用這兩個別名,所以值要對應(yīng)起來
ret = re.search(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.it123.com</h2></html>")
print(ret.group())
#報錯‘NoneType’,因為h1和h2不對應(yīng)
a1='21abc11abc2'
a2= re.search(r"(\d)(\d)abc\2", a)
print(a2.group())
#21abc1
#\2表示前邊第二個分組
b1='21abc11abcabc2'
b2 = re.search(r"(?P<id>abc){2}", a)
print(b2 .group())
#abcabc
5、斷言
從斷言的表達(dá)形式可以看出,它用的就是分組符號,只不過開頭都加了一個問號,這個問號就是在說這是一個非捕獲組,這個組沒有編號,不能用來后向引用,只能當(dāng)做斷言。
| 用法 | 用法說明 |
|---|---|
| (?=X) | 正先行斷言。僅當(dāng)子表達(dá)式 X 在 此位置的右側(cè)匹配時才繼續(xù)匹配。例如,/w+(?=/d) 與后跟數(shù)字的單詞匹配,而不與該數(shù)字匹配。此構(gòu)造不會回溯。 |
| (?!X) | 負(fù)先行斷X言。僅當(dāng)子表達(dá)式 X 不在 此位置的右側(cè)匹配時才繼續(xù)匹配。例如,例如,/w+(?!/d) 與后不跟數(shù)字的單詞匹配,而不與該數(shù)字匹配 。 |
| (?<=X) | 正后發(fā)斷言。僅當(dāng)子表達(dá)式 X 在 此位置的左側(cè)匹配時才繼續(xù)匹配。例如,(?<=19)99 與跟在 19 后面的 99 的實例匹配。此構(gòu)造不會回溯。 |
| (?<!X) | 負(fù)后發(fā)斷言。僅當(dāng)子表達(dá)式 X 不在此位置的左側(cè)匹配時才繼續(xù)匹配。例如,(?<!19)99 與不跟在 19 后面的 99 的實例匹配 |
匹配<title>xxx</title>中xxx:(?<=<title>).*(?=</title>)
自己理解就是:
- 前兩種斷言要放在
xxx的后邊寫:
(1.1)(?=X): 表示xxx后邊有X
(1.2)(?!X):表示xxx后邊沒有X - 后兩種斷言要放在
xxx的前邊寫:
(2.1)(?<=X):表示xxx前邊有X
(2.2)(?<!X):表示xxx前邊沒有X
5、例子
(1)非
^(?!.*200).*$,只匹配200
^(?!.*[200|400]).*$,只匹配200和400
[^a-z]反取,不含a-z字母的
(2)\u4e00-\u9fa5 中文
(3)r"\b([\u4e00-\u9fa5]\s?[\u4e00-\u9fa5]+)\b" # 小 明 匹配這種單字中間有空格的
二、re模塊
(一)原始字符 r的含義
- 將在python里有特殊意義的字符如\b、\r,轉(zhuǎn)換成原生字符
- 就是去除它在python的特殊意義,字符串里是啥就是啥,不會改變
a="\\hello\b\r"
b=r"\\hello\b\r"
print(a)
print(b)
# \hello
#\\hello\b\r
(二)正則匹配模式表
| 修飾符 | 描述 |
|---|---|
| re.I(re.IGNORECASE) | 使匹配對大小寫不敏感 |
| re.L(re.LOCALE) | 做本地化識別(locale-aware)匹配 |
| re.M(re.MULTILINE) | 多行匹配,影響 ^ 和 $ |
| re.S(re.DOTALL) | 使 . 匹配包括換行在內(nèi)的所有字符 |
| re.U | 根據(jù)Unicode字符集解析字符。這個標(biāo)志影響 \w, \W, \b, \B. |
| re.X | 該標(biāo)志通過給予你更靈活的格式以便你將正則表達(dá)式寫得更易于理解。 |
(三)函數(shù)
1、re.compile(pattern[, flags])
compile 函數(shù)用于編譯正則表達(dá)式,生成一個正則表達(dá)式( Pattern )對象,供 match() 和 search() 這兩個函數(shù)使用
-
pattern: 一個字符串形式的正則表達(dá)式 -
flags:可選,表示匹配模式,比如忽略大小寫,多行模式等具體為:re.I、re.L 、re.M 、re.S 、re.U 、re.X
2、re.match(pattern, string, flags=0)
match 嘗試從字符串的起始位置匹配一個模式,如果不是起始位置匹配成功的話,返回none。
-
pattern:匹配的正則表達(dá)式 -
string:要匹配的字符串。 -
flags: 可選,表示匹配模式
3、re.search(pattern, string, flags=0)
search 掃描整個字符串并返回第一個成功的匹配。
-
pattern:匹配的正則表達(dá)式 -
string:要匹配的字符串。 -
flags: 可選,表示匹配模式
re.match與re.search的區(qū)別:
- re.match只匹配字符串的
開始位置,如果字符串開始不符合正則表達(dá)式,則匹配失敗,函數(shù)返回None; - re.search匹配
整個字符串,直到找到一個匹配。
4、 re.sub(pattern, repl, string, count=0, flags=0)
正則表達(dá)式替換函數(shù)
-
pattern:匹配的正則表達(dá)式 -
repl:需要替換成的字符串或函數(shù) -
string:需要被替換的字符串 -
count:替換次數(shù) -
flags:匹配模式 - 特殊用法:
\數(shù)字和\g<數(shù)字>表示前面pattern里面第數(shù)字個分組,需要和r'\數(shù)字'聯(lián)用,否則不好用,\g<0>,\0代表前面pattern匹配到的所有字符串。
import re
text="2020年10月14日"
text=re.sub(r"[\u4e00-\u9fa5]","-",text,2).strip("日")
#2020-10-14
c= 'abc124hello46goodbye67shit'
c=re.sub(r'\d+[hg]', 'foo1', c)
# abcfoo1ellofoo1oodbye67shit
a1 = re.sub('(\d{2})-(\d{2})-(\d{4})', r'\3-\1-\2', '06-07-2018')
a2 = re.sub('(\d{2})-(\d{2})-(\d{4})', r'\g<3>-\g<2>-\g<1>', '06-07-2018')
a3 = re.sub(r'(\d{4})(\d{2})(\d{2})', r'\1-\2-\3', '20201010')
#a1=2018-06-07
#a2=2018-06-07
#a3=2020-10-10
def replace_num(str):
numDict = {
"0": "〇",
"1": "一",
"2": "二",
"3": "三",
"4": "四",
"5": "五",
"6": "六",
"7": "七",
"8": "八",
"9": "九",
}
#要用.group()取文本,因為是匹配出來的是對象
print(str.group())
return numDict[str.group()]
my_str = "2018年6月7號"
a = re.sub(r"(\d)", replace_num, my_str)
print(a)
# 二〇一八年六月七號,每次匹配一個數(shù)字,執(zhí)行函數(shù),獲取替換后的值
#匹配多行
def process(text):
import re
if "|STCNTTTP|" in text:
text=re.sub("\|STCNTTTP\|.*option.*\|STCNTTTP\|","",text,flags=re.DOTALL)
return text
4.1、subn(pattern, repl, string, count=0, flags=0)
替換匹配成功的指定位置字符串,并且返回替換次數(shù),可以用兩個變量分別接受
-
pattern: 正則模型 -
repl: 要替換的字符串 -
string: 要匹配的字符串 -
count: 指定匹配個數(shù) -
flags: 匹配模式
import re
origin = "hello alex bcd alex lge alex acd 19"
a,b = re.subn("a","替換",origin)
print(a)
print(b)
#hello 替換lex bcd 替換lex lge 替換lex 替換cd 19
#4
5、re.split(pattern, string, maxsplit=0)
- 多個 分隔符,切割功能非常強(qiáng)大
- 通過正則表達(dá)式將字符串分離。
- 如果用括號將正則表達(dá)式括起來,那么匹配的字符串也會被列入到list中返回。
- maxsplit是分離的次數(shù),maxsplit=1分離一次,默認(rèn)為0,不限制次數(shù)。
(1) 單字符切割:
re.split(';',line)
['aa bb cc dd', ' ee ff. gg- hh ii kk']
(2) 兩個字符以上切割,放在 [ ]中(不保留分隔符):
re.split('[;.-]',line)
['aa bb cc dd', ' ee ff', ' gg', ' hh ii kk']
(3) 使用( )捕獲分組(保留分割符):
re.split('([;,])',line)
['aa bb cc dd', ';', ' ee ff', '.', ' gg', '-', ' hh ii kk']
def process(text):
"""
eg:安徽省住房和城鄉(xiāng)建設(shè)廳黨組書記、廳長 趙馨群
eg:省工商局局長 朱斌 省商務(wù)廳副廳長 喬興力
空格分割,多個的,長多于3的不要
"""
import re
res=text.split()
print(res)
if len(res)>1:
return [i.replace(" ", "") for i in res if 1 < len(i) < 4]
if len(res)==1:
return [i.replace(" ", "") for i in res]
else:
return text
6、findall(string[, pos[, endpos]])
在字符串中找到正則表達(dá)式所匹配的所有子串,并返回一個列表,如果沒有找到匹配的,則返回空列表。
注意: match 和 search 是匹配一次 ,findall 匹配所有。
-
string: 待匹配的字符串。 -
pos: 可選參數(shù),指定字符串的起始位置,默認(rèn)為 0。 -
endpos: 可選參數(shù),指定字符串的結(jié)束位置,默認(rèn)為字符串的長度。
用法:
- 1、無分組:匹配所有合規(guī)則的字符串,匹配到的字符串放到一個列表中
- 2、有分組:只將匹配到的字符串里,組的部分放到列表里返回(相當(dāng)于groups()方法)
- 3、多個分組:只將匹配到的字符串里,組的部分放到一個元組中,最后將所有元組放到一個列表里返(相當(dāng)于在group()結(jié)果里再將組的部分,分別,拿出來放入一個元組,最后將所有元組放入一個列表返回)
- 4、分組中有分組:只將匹配到的字符串里,組的部分放到一個元組中,先將包含有組的組,看作一個整體也就是一個組,把這個整體組放入一個元組里,然后在把組里的組放入一個元組,最后將所有組放入一個列表返回(只要是個組就把結(jié)果放元組里,有幾個組,元組里就有幾個)
- 5、?: 在有分組的情況下findall()函數(shù),不只拿分組里的字符串,拿所有匹配到的字符串,注意?:只用于不是返回正則對象的函數(shù)如findall()
import re
#1無分組
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a\w+", origin)
print(r)
#輸出結(jié)果
#['alex', 'alex', 'alex', 'acd']
#2有分組
r = re.findall("a(\w+)", origin)
print(r)
#輸出結(jié)果
#['lex', 'lex', 'lex', 'cd']
#3多個分組
r = re.findall("(a)(\w+)", origin)
print(r)
#輸出結(jié)果
#[('a', 'lex'), ('a', 'lex'), ('a', 'lex'), ('a', 'cd')]
#4分組中有分組
r = re.findall("(a)(\w+(e))", origin)
print(r)
#輸出結(jié)果
#[('a', 'le', 'e'), ('a', 'le', 'e'), ('a', 'le', 'e')]
#5?:
b = re.findall("a(?:\w+)",origin)
print(b)
#輸出
# ['alex', 'alex', 'alex', 'acd']
#findall匹配多個值,取值
def process(text):
import re
l=re.findall(r"javascript:download\('\./(.*?\.pdf)','(.*?\.pdf)'\);",text)
ptf_l=list(l[0])
url=f"https://www.shclearing.com/wcm/shch/pages/client/download/download.jsp?FileName={ptf_l[0]}&DownName={ptf_l[1]}"
print(url)
process("javascript:download('./P020200729557188212660.pdf','漳州市九龍江集團(tuán)有限公司關(guān)于主體信用評級發(fā)生變化的公告.pdf');")
#獲取到的l是元組的格式
注意1:一旦匹配成,再次匹配,是從前一次匹配成功的,后面一位開始的,也可以理解為匹配成功的字符串,不在參與下次匹配
import re
#無分組
r = re.findall("\d+\w\d+", "a2b3c4d5") #瀏覽全部字符串,匹配所有合規(guī)則的字符串,匹配到的字符串放到一個列表中
print(r)
#輸出結(jié)果
#['2b3', '4d5']
#注意:匹配成功的字符串,不在參與下次匹配,所以3c4也符合規(guī)則但是沒匹配到
注意2:如果沒寫匹配規(guī)則,也就是空規(guī)則,返回的是一個比原始字符串多一位的,空字符串列表
import re
#無分組
r = re.findall("", "a2b3c4d5") #瀏覽全部字符串,匹配所有合規(guī)則的字符串,匹配到的字符串放到一個列表中
print(r)
#輸出結(jié)果
#['', '', '', '', '', '', '', '', '']
#注意:如果沒寫匹配規(guī)則,也就是空規(guī)則,返回的是一個比原始字符串多一位的,空字符串列表
注意3:正則匹配到空字符的情況,如果規(guī)則里只有一個組,而組后面是*就表示組里的內(nèi)容可以是0個或者多過,這樣組里就有了兩個意思:
一個意思是匹配組里的內(nèi)容,
二個意思是匹配組里0內(nèi)容(即是空白)
所以盡量避免用*否則會有可能匹配出空字符串
import re
origin = "hello alex bcd alex"
r = re.findall("(a)*", origin)
print(r)
#['', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '']
7、方法
正則表達(dá)式,返回類型為表達(dá)式對象的
如:<_sre.SRE_Match object; span=(6, 7), match='a'>
返回對象的,需要用正則方法取字符串,
-
group():group() 同group(0)就是匹配正則表達(dá)式整體結(jié)果,不管有沒有分組將匹配到的全部拿出來 -
group(num>0):返回匹配到的對應(yīng)第num個分組結(jié)果 -
groups():返回所有分組匹配的列表 =(group(1),group(2).....) -
groupdict():只返回分組中定義了key的組的結(jié)果 -
start():返回匹配開始的位置 -
end():返回匹配結(jié)束的位置 -
span():返回一個元組包含匹配 (開始,結(jié)束) 的位置
import re
#無分組
origin = "hello alex bcd alex lge alex acd 19"
r = re.search("a\w+", origin)
r1 = re.search("a(\w+).*(\d)", origin)
r2 = re.search("a(?P<n1>\w+).*(?P<n2>\d)", origin)
# group獲取匹配到的所有結(jié)果,不管有沒有分組將匹配到的全部拿出來
print(r.group())
print(r1.group())
print(r1.group(2))
print(r2.group())
# groups獲取模型中匹配到的分組結(jié)果,只拿出匹配到的字符串中分組部分的結(jié)果
print(r.groups())
print(r1.groups())
print(r2.groups())
# 獲取模型中匹配到的分組結(jié)果,只拿出匹配到的字符串中分組部分定義了key的組結(jié)果
print(r.groupdict())
print(r1.groupdict())
print(r2.groupdict())
r3 = re.search("(h).*a(?P<n1>\w+).*(?P<n2>\d)", origin)
print(r3.groupdict())
#alex
#alex bcd alex lge alex acd 19
#9
#alex bcd alex lge alex acd 19
#()
#('lex', '9')
#('lex', '9')
#{}
#{}
#{'n1': 'lex', 'n2': '9'}
#{'n1': 'cd', 'n2': '9'}