一、正则表达式
re模块是python独有的匹配字符串的模块,该模块中提供的很多功能是基于正则表达式实现的,
而正则表达式是对字符串进行模糊匹配,提取自己需要的字符串部分,他对所有的语言都通用。
- re模块是python独有的
- 正则表达式所有编程语言都可以使用
- re模块、正则表达式是对字符串进行操作
(一)常用正则
1、字符
元字符 | 匹配内容 |
---|---|
. | 匹配除换行符以外的任意字符 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
a|b | 匹配字符a或字符b |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
() | 匹配括号内的表达式,也表示一个组 |
(?:表达式) | 取消括号的分组功能 |
2、字符集
正则 | 说明 |
---|---|
\d | 匹配数字,它相当于类[0-9] |
\D | 匹配非数字,它相当于类[^0-9] |
\s | 匹配任意的空白符,它相当于类[\t\n\r\f\v] |
\S | 匹配非空白符,它相当于类[^\t\n\r\f\v] |
\w | 匹配字母或数字或下划线,它相当于类[a-zA-Z0-9_] |
\W | 匹配非字母或数字或下划线,它相当于类[^a-zA-Z0-9_] |
\b | 匹配位于开始或结尾的空字符串 |
\B | 匹配不位于开始或结尾的空字符串,它相当于类[^\b] |
\A | 仅匹配字符串开头,同^ |
\Z | 仅匹配字符串结尾,同$ |
3、量词
贪婪模式:总是尝试匹配尽可能多的字符
非贪婪则相反,总是尝试匹配尽可能少的字符。
量词 | 用法说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次至无限次 |
{n,m} | 重复n到m次 |
{0,}
匹配前一个字符0
或多次,等同于*
元字符
{+,}
匹配前一个字符1
次或无限次,等同于+
元字符
{0,1
}匹配前一个字符0
次或1
次,等同于?
元字符
如果()
后面跟的是特殊元字符如 (adc)*
那么*控制的前导字符就是()
里的整体内容,不再是前导一个字符
4、特殊分组用法表:只对正则函数返回对象的有用
用法 | 用法说明 |
---|---|
(?P<name>) | 分组起别名,?P<>定义组里匹配内容的key(键),<>里面写key名称,值就是匹配到的内容,用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,后边引用这两个别名,所以值要对应起来
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不对应
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、断言
从断言的表达形式可以看出,它用的就是分组符号,只不过开头都加了一个问号,这个问号就是在说这是一个非捕获组,这个组没有编号,不能用来后向引用,只能当做断言。
用法 | 用法说明 |
---|---|
(?=X) | 正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。 |
(?!X) | 负先行断X言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,而不与该数字匹配 。 |
(?<=X) | 正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。 |
(?<!X) | 负后发断言。仅当子表达式 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,转换成原生字符
- 就是去除它在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) | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
(三)函数
1、re.compile(pattern[, flags])
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用
-
pattern
: 一个字符串形式的正则表达式 -
flags
:可选,表示匹配模式,比如忽略大小写,多行模式等具体为:re.I、re.L 、re.M 、re.S 、re.U 、re.X
2、re.match(pattern, string, flags=0)
match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,返回none。
-
pattern
:匹配的正则表达式 -
string
:要匹配的字符串。 -
flags
: 可选,表示匹配模式
3、re.search(pattern, string, flags=0)
search 扫描整个字符串并返回第一个成功的匹配。
-
pattern
:匹配的正则表达式 -
string
:要匹配的字符串。 -
flags
: 可选,表示匹配模式
re.match与re.search的区别:
- re.match只匹配字符串的
开始位置
,如果字符串开始不符合正则表达式,则匹配失败,函数返回None; - re.search匹配
整个字符串
,直到找到一个匹配。
4、 re.sub(pattern, repl, string, count=0, flags=0)
正则表达式替换函数
-
pattern
:匹配的正则表达式 -
repl
:需要替换成的字符串或函数 -
string
:需要被替换的字符串 -
count
:替换次数 -
flags
:匹配模式 - 特殊用法:
\数字
和\g<数字>
表示前面pattern里面第数字
个分组,需要和r'\数字'
联用,否则不好用,\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)
# 二〇一八年六月七号,每次匹配一个数字,执行函数,获取替换后的值
#匹配多行
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)
替换匹配成功的指定位置字符串,并且返回替换次数,可以用两个变量分别接受
-
pattern
: 正则模型 -
repl
: 要替换的字符串 -
string
: 要匹配的字符串 -
count
: 指定匹配个数 -
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)
- 多个 分隔符,切割功能非常强大
- 通过正则表达式将字符串分离。
- 如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。
- maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。
(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:安徽省住房和城乡建设厅党组书记、厅长 赵馨群
eg:省工商局局长 朱斌 省商务厅副厅长 乔兴力
空格分割,多个的,长多于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]])
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 ,findall 匹配所有。
-
string
: 待匹配的字符串。 -
pos
: 可选参数,指定字符串的起始位置,默认为 0。 -
endpos
: 可选参数,指定字符串的结束位置,默认为字符串的长度。
用法:
- 1、无分组:匹配所有合规则的字符串,匹配到的字符串放到一个列表中
- 2、有分组:只将匹配到的字符串里,组的部分放到列表里返回(相当于groups()方法)
- 3、多个分组:只将匹配到的字符串里,组的部分放到一个元组中,最后将所有元组放到一个列表里返(相当于在group()结果里再将组的部分,分别,拿出来放入一个元组,最后将所有元组放入一个列表返回)
- 4、分组中有分组:只将匹配到的字符串里,组的部分放到一个元组中,先将包含有组的组,看作一个整体也就是一个组,把这个整体组放入一个元组里,然后在把组里的组放入一个元组,最后将所有组放入一个列表返回(只要是个组就把结果放元组里,有几个组,元组里就有几个)
- 5、?: 在有分组的情况下findall()函数,不只拿分组里的字符串,拿所有匹配到的字符串,注意?:只用于不是返回正则对象的函数如findall()
import re
#1无分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a\w+", origin)
print(r)
#输出结果
#['alex', 'alex', 'alex', 'acd']
#2有分组
r = re.findall("a(\w+)", origin)
print(r)
#输出结果
#['lex', 'lex', 'lex', 'cd']
#3多个分组
r = re.findall("(a)(\w+)", origin)
print(r)
#输出结果
#[('a', 'lex'), ('a', 'lex'), ('a', 'lex'), ('a', 'cd')]
#4分组中有分组
r = re.findall("(a)(\w+(e))", origin)
print(r)
#输出结果
#[('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','漳州市九龙江集团有限公司关于主体信用评级发生变化的公告.pdf');")
#获取到的l是元组的格式
注意1:一旦匹配成,再次匹配,是从前一次匹配成功的,后面一位开始的,也可以理解为匹配成功的字符串,不在参与下次匹配
import re
#无分组
r = re.findall("\d+\w\d+", "a2b3c4d5") #浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中
print(r)
#输出结果
#['2b3', '4d5']
#注意:匹配成功的字符串,不在参与下次匹配,所以3c4也符合规则但是没匹配到
注意2:如果没写匹配规则,也就是空规则,返回的是一个比原始字符串多一位的,空字符串列表
import re
#无分组
r = re.findall("", "a2b3c4d5") #浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中
print(r)
#输出结果
#['', '', '', '', '', '', '', '', '']
#注意:如果没写匹配规则,也就是空规则,返回的是一个比原始字符串多一位的,空字符串列表
注意3:正则匹配到空字符的情况,如果规则里只有一个组,而组后面是*
就表示组里的内容可以是0
个或者多过,这样组里就有了两个意思:
一个意思是匹配组里的内容,
二个意思是匹配组里0
内容(即是空白)
所以尽量避免用*
否则会有可能匹配出空字符串
import re
origin = "hello alex bcd alex"
r = re.findall("(a)*", origin)
print(r)
#['', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '']
7、方法
正则表达式,返回类型为表达式对象的
如:<_sre.SRE_Match object; span=(6, 7), match='a'>
返回对象的,需要用正则方法取字符串,
-
group()
:group() 同group(0)就是匹配正则表达式整体结果,不管有没有分组将匹配到的全部拿出来 -
group(num>0)
:返回匹配到的对应第num个分组结果 -
groups()
:返回所有分组匹配的列表 =(group(1),group(2).....) -
groupdict()
:只返回分组中定义了key的组的结果 -
start()
:返回匹配开始的位置 -
end()
:返回匹配结束的位置 -
span()
:返回一个元组包含匹配 (开始,结束) 的位置
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获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来
print(r.group())
print(r1.group())
print(r1.group(2))
print(r2.group())
# groups获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果
print(r.groups())
print(r1.groups())
print(r2.groups())
# 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果
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'}