首页 软件代码

PYTHON正则学习记录


本文声明

1.本文内容来自个人编写、python官方文档、参考菜鸟教程和网上部分解析。
2.本文加入大部分个人理解,并删去部分官方解释。可能存在理解错误,造成本文存在错误。

特殊符号

特殊符号代表含义
\转义字符,用于转义特殊符号. * ? 等,可以通过\. \* \?来进行匹配相对应字符
.在默认模式,匹配除了换行的任意字符。
*限定,对它前面的正则式匹配0到任意次重复, 尽量多的匹配字符串。
+限定,对它前面的正则式匹配1到任意次重复。 尽量多的匹配字符串。
?限定, 对它前面的正则式匹配0到1次重复。
当位于* + {} 等之后则是限制,尽可能的少匹配字符串。
^定位,匹配字符串的开头
当形式为[^...]时,则表示不匹配...中的字符
$定位,匹配字符串尾
|或字符,A|B, A 和 B 可以是任意正则表达式,创建一个正则表达式,匹配 A 或者 B. 任意个正则表达式可以用 '|' 连接。
{}限定,{m}对它前面的正则式匹配m次,少于m个无法匹配。
{m,n}对它前面的正则式匹配至少m次最多n次,尽量多的匹配字符串。
[]集合,例如[abc]则进行匹配'a'、'b'、'c'中任意字符,其余写法:
[0-9]:0到9任意数字。[a-z]:a到z任意字符,除此之外还有[1-5]、[0-9A-Za-z]等多种多样。
当形式为[^...]时,则表示不匹配...中的字符
()组合,匹配括号内的任意正则表达式,会储存匹配结果。()内看作一个整体,子表达式

符号组合

组合代表含义
(?#...)...为注释,无作用的正则表达式
(?:...)...为表达式(...通常时 | 的语句) 用来匹配字符串,但不保存匹配的(?:...)的结果。
(?=...)...为自定义正则表达式。整体位置在表达式之后,将从后向前匹配,如果后面字符为...,则开始匹配表达式,匹配结果不包括...
(?!...)...为自定义正则表达式。整体位置在表达式之后,将从后向前匹配,如果后面字符为...,则不匹配表达式,不是…则开始匹配
(?<=...)...为自定义正则表达式。整体位置在表达式之前 ,将从前向后匹配,如果前面字符为...,则开始匹配表达式,匹配结果不包括...
(?<!...)...为自定义正则表达式串。整体位置在表达式之前 ,将从前向后匹配,如果前面字符为...,则不匹配表达式,不是…则开始匹配
(?P<name>...)...为自定义正则表达式...中匹配的字符串将赋值给name,可以通过group('name')进行读取访问,案例在下方代码区
(?P=name)通过name传递的字符串来匹配。使用此规则,必须先用(?P…)确定name的值,之后才能使用,案例在下方代码区
(?(id/name)yes-...|no-...)通过判断id或name是否匹配成功,来决定选择yes-...或no-...表达式,例如(?P<name><)h1(?(name)>|$)来匹配<h1>或h1,当(?P<name>>)匹配到<后,则?(name)会匹配yes-...反之匹配no-...

案例
请阅读完下方函数使用方法后观看案例

text='iammebyz.cn mebyz'
a=re.search(r'(?#我是半叶子,注释)mebyz',text)
print(a.group())
b=re.search(r'(?:m|e)byz',text)
print(b.group())
c=re.search(r'm(?=m)',text)#对比c、d观察m的位置
print(c)
d=re.search(r'm(?!m)',text)#对比c、d观察m的位置
print(d)
e=re.search(r'(?<=a)m',text)#对比e、f观察m的位置
print(e)
f=re.search(r'(?<!a)m',text)#对比e、f观察m的位置
print(f)
g=re.search(r'(?P<name>me)byz',text)#name为定义的名称,可随意
print(g['name'],":",g.group())
h=re.search(r'(?P<name>me).*?(?P=name)',text)
print(h['name'],":",h.group())
i=re.search(r'(?P<name>me).*?(?(name)cn|me)',text)
print(i['name'],":",i.group())
#运行结果如下:
#a:mebyz
#b:ebyz
#c:<re.Match object; span=(2, 3), match='m'>
#d:<re.Match object; span=(3, 4), match='m'>
#e:<re.Match object; span=(2, 3), match='m'>
#f:<re.Match object; span=(3, 4), match='m'>
#g:me : mebyz
#h:me : mebyz.cn me
#i:me : mebyz.cn

优先级

运算符描述
\转义符
(), (?:), (?=), []圆括号和方括号
*, +, ?, {n}, {n,}, {n,m}限定符
^, $, 任何元字符(例:\?则是字符?)、任何字符定位点和序列(即:位置和顺序)
|替换,"或"操作

特殊转义字母

转义字母含义
\w匹配字母数字及下划线
\W匹配非字母数字及下划线
\s匹配任意空白字符,等价于 [ \t\n\r\f]。
\S匹配任意非空字符
\d匹配任意数字,等价于[0-9]
\D匹配任意非数字
\A匹配字符串开始,和^相似
\Z匹配字符串结束
\b匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\numbernumber指的是一个数,匹配数字代表的组合。每个括号是一个组合,组合从1开始编号,最大为99。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在 '[' 和 ']' 字符集合内,任何数字转义都被看作是字符。
\g<id/name>需要与Match.expand()方法连用才可,可见案例3

案例1

text='''mebyz123_=-+!@#$%^&*(),./;'[]\。我是半叶子 iam
mebyz'''
a=re.search(r'\w+',text)
print(a)
b=re.search(r'\W+',text)
print(b)
c=re.search(r'\s+',text)
print(c)
d=re.search(r'\S+',text)
print(d)
e=re.search(r'\d+',text)
print(e)
f=re.search(r'\D+',text)
print(f)
g=re.search(r'\A.*',text)
print(g)
h=re.search(r'.\Z',text)
print(h)
i=re.search(r'.+\b',text)
print(i)
j=re.search(r'.+\B',text)
print(j)
#运行结果:
#a:<re.Match object; span=(0, 9), match='mebyz123_'>
#b:<re.Match object; span=(9, 31), match="=-+!@#$%^&*(),./;'[]\\。">
#c:<re.Match object; span=(36, 37), match=' '>
#d:<re.Match object; span=(0, 36), match="mebyz123_=-+!@#$%^&*(),./;'[]\\。我是半叶子">
#e:<re.Match object; span=(5, 8), match='123'>
#f:<re.Match object; span=(0, 5), match='mebyz'>
#g:<re.Match object; span=(0, 40), match="mebyz123_=-+!@#$%^&*(),./;'[]\\。我是半叶子 iam">
#h:<re.Match object; span=(45, 46), match='z'>
#i:<re.Match object; span=(0, 40), match="mebyz123_=-+!@#$%^&*(),./;'[]\\。我是半叶子 iam">
#j:<re.Match object; span=(0, 39), match="mebyz123_=-+!@#$%^&*(),./;'[]\\。我是半叶子 ia">

案例2
()的判断方法:从左到右,左括号在最左面为1,其次为2,以此类推。(此括号不包括(?…)扩展标记方法在内。)

text='h1 i am byz h1 mebyz.cn'
a=re.search(r'(h1).*?(byz) \1.*?\2',text)
print(a[1],a[2])
print(a.group())
#运行结果:
#h1 byz
#h1 i am byz h1 mebyz

案例3

text='2020年08月'
x=re.search(r'(\d{2,}).+',text)
print(x.expand(r'\g<1>你好'))
y=re.search(r'(?P<name>\d{2,}).+',text)
print(y.expand(r'\g<name>你好'))
#输出结果如下:
#2020你好
#2020你好

python re模块方法

(仅列举常见常用的)
以下为各方法中参数的含义
pattern:正则表达式
string:待匹配字符串
flags为:用于控制正则表达式的匹配方式(可省略)

re.match()

re.match(pattern, string, flags=0) 从字符串的起始位置匹配,如果不是起始位置匹配成功的话,match()就返回none。如果从开头开始到正则匹配结束全部符合,则返回匹配结果。
实例:m.{2,4}表示:匹配m开始后面进跟着2到4个任意字符(除换行符),且尽可能的为4个(贪婪模式)

text='mebyz.cn'
x=re.match(m'i.{2,4}', text)#开头为m
print(x)
y=re.match(r'a.{2,4}', text)#开头为a
print(y)
#输出结果如下:
#x:<re.Match object; span=(0, 5), match='mebyz'>
#y:None

输出结果表明:当字符串开头为不符合情况则返回none,如果全部符合则返回字符的位置和字符串,可以用x.group()取出匹配结果:mebyz

re.seach()

re.seach(pattern, string, flags=0) 从字符串的起始位置匹配,如果不是起始位置匹配成功的话,则跳到下一个字符开始匹配,以此类推,直到成功匹配或匹配失败返回none。

text='mebyz.cn'
x=re.search(r'm.{2,4}', text)
print(x)
y=re.search(r'e.{2,4}', text)
print(y)
z=re.search(r'a.{2,4}', text)
print(z)
#输出结果如下:
#x:<re.Match object; span=(0, 5), match='mebyz'>
#y:<re.Match object; span=(1, 6), match='ebyz.'>
#z:None

输出结果表明:如果开头符合则进行匹配,匹配成功则返回结果,开头不符合则进行下一个字符来匹配,以此类推。当全部都不符合后则返回None,可以用x.group()取出匹配结果:mebyz

re.finditer()

re.finditer(pattern, string, flags=0)正则表达式在 string 里所有的非重复匹配,返回为一个迭代器保存匹配对象。string 从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。
可以看作是re.search()升级版,re.search()是匹配到第一个成功字符串后就停止,而re.finditer()则是全部进行匹配,并返回一个迭代器(可以用for i in x等取出)

text='2020年08月04日'
x=re.finditer(r'(\d{2,4})(年|月|日)', text)
for i in x:
    print(i.group())
y=re.search(r'(\d{2,4})(年|月|日)', text)
print(y.group())
#输出结果如下:
#i:2020年
#    08月
#    04日
#y:2020年

输出结果表明:结果re.finditer()将全部符合的全进行匹配,而re.search()匹配首个符合正则的字符串。

group()和groups()的用法

group()

  1. group()为group(0),表示含义为整体表达式所取到的字符串。
  2. group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分,以此类推。
  3. ()的判断方法:从左到右,左括号在最左面为1,其次为2,以此类推。(此括号不包括(?…)扩展标记方法在内。)

groups()

groups() 所有()内的匹配部分的结果的元组集合。可以看作是为元组(group(1),group(2), .....group(n)) n等于()的个数

举例

text='2020年08月04日'
y=re.search(r'(\d{2,4})(年|月|日)', text)
print(y.group(),y.group(1),y.group(2))
print(y.groups())
#输出结果如下:
#2020年 2020 年
#('2020', '年')

结果表明:
group()(等价与group(0))输出为整个表达式的值,group(1)为第一个()匹配的结果,group(2)为第二个()匹配的结果。
groups()则输出所有的()所匹配的结果构成的元组。

re.findall()

re.findall(pattern, string, flags=0)对 string 返回一个不重复的的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。
1.如果正则表达式中无()则返回整体正则表达式 匹配列表。
2.如果表达式中有一个(),则返回正则表达式中()内的匹配结果。
3.如果大于一个(),则返回一个列表元素是元组的列表。
记:(?...)不属于()

text='2020年08月04日'
x=re.findall(r'\d{2,4}(?:年|月|日)', text)
print(x)
y=re.findall(r'\d{2,4}(年|月|日)', text)
print(y)
z=re.findall(r'(\d{2,4})(年|月|日)', text)
print(z)
#输出结果如下:
#x:['2020年', '08月', '04日']
#y:['年', '月', '日']
#z:[('2020', '年'), ('08', '月'), ('04', '日')]

re.split()

re.split(pattern, string, maxsplit=0, flags=0)用pattern分开 string 。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。

text='2020-08-04'
x=re.split('-',text)
print(x)
text1='2020年08月04日'
x1=re.split('年|月|日',text1)
print(x1)
#输出结果如下:
['2020', '08', '04']
['2020', '08', '04', '']

re.sub()

re.sub(pattern, repl, string, count=0, flags=0)
返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果样式没有找到,则不加改变地返回 string。
repl 可以是字符串或函数;
如为字符串,则将进行常规替换。
如为函数,传递给函数返回本次传递匹配的结果(需用group()取出字符串),函数返回的结果为替换值

def resub(re_str):
    if re_str[0]=='年':
        return '-'
    elif re_str[0]=='月':
        return '--'
    else:
        return ''
text='2020年08月04日'
x=re.sub('年|月|日', '-', text, count=0)
print(x)
y=re.sub('年|月|日', resub, text, count=0)
print(y)
#运行结果如下:
#2020-08-04-
#2020-08--04

re.subn()

re.subn(pattern, repl, string, count=0, flags=0)
行为与 sub() 相同,但是返回一个元组 (字符串, 替换次数)

re.escape()

转义 re.escape(pattern) 中的特殊字符。

x=re.escape('?*+')
print(x)
#运行及如果如下:
#\?\*\+

re.purge()

清除正则表达式的缓存。

re.compile()方法

re.compile(pattern, flags=0)将正则表达式的样式编译为一个正则表达式对象(Pattern),可以用于匹配,通过这个对象的方法 match(), search() 以及其他如下描述。

Pattern.split()

Pattern.split(string, maxsplit=0)等价于 split() 函数,使用了编译后的样式。

Pattern.findall()

Pattern.findall(string[, pos[, endpos]])类似函数 findall() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。

text='2020年08月'
m=re.compile(r'\d{2,}.*')
x=m.findall(text,pos=5,endpos=7)
print(x)
#运行结果如下:
#['08']

Pattern.finditer()

Pattern.finditer(string[, pos[, endpos]])类似函数 finiter() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。
案例见:Pattern.findall()

Pattern.sub()

Pattern.sub(repl, string, count=0)等价于 sub() 函数,使用了编译后的样式。

Pattern.subn()

Pattern.subn(repl, string, count=0)等价于 subn() 函数,使用了编译后的样式。

Pattern.flags

正则匹配标记。这是可以传递给 compile() 的参数,任何 (?…) 内联标记,隐性标记比如 UNICODE 的结合。

Pattern.groups

捕获到的模式串中组的数量。

Pattern.groupindex

映射由 (?P) 定义的命名符号组合和数字组合的字典。如果没有符号组,那字典就是空的。

m=re.compile(r'(?P<name>\d{2,})(?P<name1>.*)')
print(m.groupindex)
#运行结果如下:
#{'name': 1, 'name1': 2}

Pattern.pattern

编译对象的原始样式字符串。

Match方法

Match.expand(template)

对 template 进行反斜杠转义替换并且返回,就像 sub() 方法中一样。转义如同 \n 被转换成合适的字符,数字引用(\1, \2)和命名组合(id或name) 替换为相应组合的内容。

text='2020年08月'
x=re.search(r'(\d{2,}).+',text)
print(x.expand(r'\g<1>你好'))
y=re.search(r'(?P<name>\d{2,}).+',text)
print(y.expand(r'\g<name>你好'))
#输出结果如下:
#2020你好
#2020你好

Match.group()

Match.group([group1, ...])(类似x.group(0,1))返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。

Match.groups()

Match.groups(default=None)返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None。

Match.groupdict()

Match.groupdict(default=None)返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None。 例如

m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
print(m.groupdict())
#运行结果如下:
#{'first_name': 'Malcolm', 'last_name': 'Reynolds'}

Match.start()和Match.end()

Match.start([group])和Match.end([group])返回 group 匹配到的字串的开始和结束标号。
group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1 。

m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
print(m.start(1),m.end(1))
#运行结果如下:
#0 7

对于一个匹配对象 m, 和一个未参与匹配的组 g ,组 g (等价于 m.group(g))产生的匹配是
m.string[m.start(g):m.end(g)]

#这个例子会从email地址中移除掉 remove_this
email = "tony@tiremove_thisger.net"
m = re.search("remove_this", email)
print(email[:m.start()] + email[m.end():])
#运行结果如下:
'tony@tiger.net'

Match.span([group])

对于一个匹配 m , 返回一个二元组 (m.start(group), m.end(group)) 。
注意如果 group 没有在这个匹配中,就返回 (-1, -1) 。group 默认为0,就是整个匹配。

Match.pos

pos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎开始在字符串搜索一个匹配的索引位置。

Match.endpos

endpos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎停止在字符串搜索一个匹配的索引位置。

Match.lastindex

捕获组的最后一个匹配的整数索引值,或者 None 如果没有匹配产生的话。比如,对于字符串 'ab',表达式 (a)b, ((a)(b)), 和 ((ab)) 将得到 lastindex == 1 , 而 (a)(b) 会得到 lastindex == 2 。

Match.lastgroup

最后一个匹配的命名组名字,或者 None 如果没有产生匹配的话。

Match.re

返回产生这个实例的 正则对象 , 这个实例是由 正则对象的 match() 或 search() 方法产生的。

Match.string

传递到 match() 或 search() 的字符串。

表达式修饰符

修饰符

及参数中的flags部分,可以设置零个或多个

符号代表含义
re.I(?i)设置后,使匹配对大小写不敏感
re.M(?m)设置后,样式字符 ^ 匹配字符串的开始,和每一行的开始(换行符后面紧跟的符号);样式字符 $ 匹配字符串尾,和每一行的结尾(换行符前面那个符号)。()内为re.M设置后生效的
re.S(?s)让 '.' 特殊字符匹配任何字符,包括换行符;如果没有这个标记,'.' 就匹配 除了 换行符的其他任意字符。
re.X(?x)这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在 *?, (?: or (?P<...> 分组之内。当一个行内有 # 不在字符集和转义序列,那么它之后的所有字符都是注释。
re.L(?L)做本地化识别(locale-aware)匹配
re.A(?a)让 \w, \W, \b, \B, \d, \D, \s 和 \S 只匹配ASCII,而不是Unicode。
re.U(?u)根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.DEBUG显示编译时的debug信息,没有内联标记。

(re.L官方不推荐使用,re.U是为了向下兼容而保留,python3默认为Unicode,因此无效。)

内联写法

(?imsx):作用域为全表达式,写在表达式开头,例如(?i)[a-z]+则是忽略字符串大小写进行匹配,作用与传递参数相同。可以一次性传递多个例如(?imsx)则传递4个修饰符过去,可以自己根据需求决定传递哪些修饰符(共7种)。
(?imsx:...):作用域为括号内...部分,例如(?i:[a-z]+)则是忽略字符串大小写进行匹配。可以一次性传递多个例如(?imsx:[a-z]+)则传递4个修饰符过去。
(?-imsx:...):作用域为括号内...部分,例如(?-i:[a-z]+)是取消忽略字符大小写进行匹配,可以一次性传递多个例如(?-imsx:[a-z]+)则传递4个修饰符过去。

案例

举例分析(仅针对re.I、re.M、re.S、re.X)
re.I作用:

text='Mebyz'
x=re.search(r'[a-z]+', text)
print(x.group())
y=re.search(r'[a-z]+', text,re.I)
print(y.group())
#输出结果如下:
#ebyz
#Mbyz

re.M作用:

text='''me
byz'''
x=re.search(r'^byz$', text)
print(x)
y=re.search(r'^byz$', text,re.M)
print(y.group())
#输出结果如下:
#None
#byz

re.S作用:

text='''me
byz'''
x=re.search(r'.{2,4}', text)
print(x)
y=re.search(r'.{2,4}', text,re.S)
print(y)
#输出结果如下(为表现出换行符\\n,则直接输出x,y):
#x:<re.Match object; span=(0, 2), match='me'>
#y:<re.Match object; span=(0, 4), match='me\nb'>(\n为换行符)

re.X作用:

text='mebyz'
x=re.search(r'.{2,4}', text)
print(x.group())
y=re.search(r'''.#   我是半叶子,
                {2,4}#这是注释
                ''', text,re.X)
print(y.group())
#输出结果如下:
#meby
#meby

内联形式写法

text='Mebyz.Cn'
m=re.findall(r'[a-z]+\.[a-z]*', text)#无限制符
print(m)
e=re.findall(r'[a-z]+\.[a-z]*', text,re.I)#参数传递形式
print(e)
b=re.findall(r'(?i)[a-z]+\.[a-z]*', text)#内联全局形式
print(b)
y=re.findall(r'(?i:[a-z]+)\.[a-z]*', text)#内联()内作用形式
print(y)
z=re.findall(r'(?i)[a-z]+\.(?-i:[a-z]*)', text)#内联()取消全局形式
print(z)
#输出结果如下:
#m:['ebyz.']
#e:['Mebyz.Cn']
#b:['Mebyz.Cn']
#y:['Mebyz.']
#z:['Mebyz.']




文章评论