正则表达式

一、search()和match()的区别:

re.search 扫描整个字符串并返回第一个成功的匹配。可以在任何位置匹配。

re.search(pattern, string, flags=0)

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

re.match(pattern, string, flags=0)

如果:pattern = '^...'  这里加了一个开始限定符 ^ ,那么search和match作用一样。

注意:search( ) 和 match() 都只能匹配一次,匹配到结果后立即返回,要匹配多次请看下面:

 

二、findall() 和 finditer()

可以匹配多次

import re
text = '你们好Hello好你234\n1ddsdf2在llo好'
p = ''
print(re.findall(p,text))  # 返回一个list ['好', '好', '好']
m = re.finditer(p,text)
print(m) # <class 'callable_iterator'>
for i in m:
    print(i) # <re.Match object; span=(8, 9), match='好'>
    print(i.group())

# --------------------------------
s = 'ddabcabcabceee'
r = '(abc){3}'
print(re.findall(r, s))  # ['abc']
r = '(?:abc){3}'
print(re.findall(r, s))  ['abcabcabc']

 

 

 

三、检索和替换

Python 的re模块提供了re.sub用于替换字符串中的匹配项。

语法:re.sub(pattern, repl, string, count=0, flags=0)

参数:
    pattern : 正则中的模式字符串。
    repl : 替换的字符串,也可为一个函数。
    string : 要被查找替换的原始字符串。
    count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
    flags : 编译时用的匹配模式,数字形式。

#!/usr/bin/python3
import re
 
phone = "2004-959-559 # 这是一个电话号码"
 
# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)
 
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)

输出:电话号码 :  2004-959-559   电话号码 :  2004959559

repl 参数是一个函数
以下实例中将字符串中的匹配的数字乘于 2:

import re
 
# 将匹配的数字乘于 2
def double(matched):
    value = int(matched.group('value'))
    return str(value * 2)
 
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s))

执行输出结果为:A46G8HFD1134

 

四、split( ) 字符串分隔:结果将返回一个list。

注意:split() str和re都有该方法,该方法中的第一个参数可以是str,也可以是pattern(正则表达式模式)
字符串分割使用 split()函数, 该函数按照匹配的子字符串进行字符串分割,返回字符串列表 (list) 对象。
re.split(pattern, string,  maxsplit=0 , flags=0)

 

五、compile 函数

为了提高效率, 还可以对 Python 正则表达式进行编译。编译的正则表达式可以重复使用 ,这样能减少正则表 达式的解析和验证,提高效率
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法格式为:
re.compile(pattern[, flags])

注意:编译参数flags(可选)要在编译的时候就写进去,编译以后,方法 search()、 match()、 findall() 和 finditer() 中的参数为:

string[, pos[, endpos],多了两个可选参数:pos 和 endpos

pos 为开始查找的索引 , 参数 endpos 为结束查找的索引

因为索引可以自定义,因此:编译后的match()可以指定匹配位置,不一定非得从第一个字符开始匹配

import re

phone = "2004-959-559 # 这是一个电话号码"
# 非数字的内容
p = '\D'
regex = re.compile(p, flags=re.M)  # 编译标志flags为可选
print(type(regex)) # <class 're.Pattern'>
match = regex.match(phone, 13)  # 可以定义索引起始范围

 

六、编译标志 flags

 flags 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:

    re.I 忽略大小写

    re.A 只匹配ASCII码
    re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
    re.M 多行模式  多行模式对于元字符 ^ 和 $ 行 为会产生影响。默认情况下^ 和$ 匹配字符串的开始和结束,而在多行模式下 ^ 和 $ 匹配任意 一行的开始和结束
    re.S 或 re.DOTALL 即为' . '包括换行符在内的任意字符(' . '本来是不包括换行符)
    re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库 ,可以匹配汉字
    re.X  或 re.VERBOS 详细模式,为了增加可读性,忽略空格和' # '后面的注释

当需要设置多 编译标志时, 编译标志之间需要位或运算符“ |”

 

 七、一个完整的例子:

import re

s = "+aaa.jpg,bbb.jpg,ccc.bmp"
# r = r"\w+(?:.jpg)"  # findall能捕获到整个表达式,math.group(1)将抛出错误,因为没有可以捕获的组。
r = r"\w+(.jpg)"  # findall只能捕获到括号里面的
r = r"\w+(?:.jpg)"  # ?: 表示非捕获模式


math = re.search(r, s)  # search()可以从任意位置开始匹配,
# math = re.match(r, s)  # match() 必须从开头开始匹配,相当于search('^xxxxxx',....)
print(math)  # <re.Match object; span=(1, 8), match='aaa.jpg'>
print(math.span())  # (1, 8) 匹配范围
print(math.start())  # 1 开始
print(math.end())  # 8 结束


print(math.group())  # aaa.jpg  无论是不是捕获模式,都能匹配整个表达式。
# print(math.group(1))  # .jpg  ?:非捕获模式下出错:IndexError: no such group
print(math.groups())  # ('.jpg',) ?:非捕获模式下:()

# 说明:findall捕获到的是组(即括号里面的),没有组时能捕获到整个表达式。
list = re.findall(r, s)
print(type(list))  # <class 'list'>
print(list)  # ['.jpg', '.jpg']    ?:非捕获模式下:() ['aaa.jpg', 'bbb.jpg']


r = '34|2b|1d'  # 34 或者 2b 或者 1d
# r = '[34bd]'  # 3或4或b或d,其中的任意一个
r = '[(34)(bd)]' # 等价于 '[34bd]',()没有任何意义。同理,[(34bd)],[^(34)(bd)]的小括号也没有意义,总结:[]里面的()没有意义。
r = '[^012345]'  # 不能是[]里面的任意一个,

# ???不能包含ab 和cd怎么写。
# 截取aaa前面的字符
s = 'a121v121faaa111'
r = '(\w+)aaa'
print(re.search(r, s).group(1))

# r = '(121)(12)'
# math= re.search(r, s)
# print(math.group())  # 12112
# print(math.group(2))  # 12
# print(math.groups())  # ('121', '12')

s = "f<abc>ff<abc>8888888<cba>dd<cba>2fdd"
r = '<abc>(?!.+<abc>).+?<cba>'
print(re.findall(r, s))
print(re.search(r, s))

r = r"\w+@qq\.com"
s = "2833@qq.com asdfsdf@qq.com"

regex = re.compile(r)
m1 = regex.search(s, 8)  # 从第8个字符开始的任意位置匹配
m = regex.match(s, 2)  # 从第2个字符开始匹配。
print(m1)

 非捕获模式

import re
# 关于正则表达式的捕获模式。
# 模式一:
# (pattern) 平时最常用的模式,在此不作过多解释。

# 模式二:(非捕获组)
# (?:pattern) 匹配但不捕获匹配结果。也就是说这是一个非捕获匹配,不存储供以后使用的匹配。否则可能会被$1或\1捕获到

# 模式三:其实也是非捕获组的扩展。
# (?=pattern) 正向预查,在任何匹配pattern的字符串开始处匹配查找字符串.这是一个非捕获匹配
# .例如:/windows(?=95|98|2000)/能匹配windows2000中的windows,但不能匹配windowsNT中的windows。
# 预查不消耗字符。例:

# 模式四:其实也是非捕获组的扩展。
# (?!pattern) 反向预查。!就是不包括,不等于的意思,例:
# 预查不消耗字符。例:

# 以下列出 ?=、?<=、?!、?<!= 的使用区别
# 本文采用findall()查找,如果是search()的话只查找到所有匹配项的第一个。

# exp1(?=exp2):查找 exp2 前面的 exp1。
# (?=exp2)exp1:不常用,相当于查找exp2开头的exp1

# exp1(?!exp2):查找后面不是 exp2 的 exp1。
# (?!exp2)exp1:查找不是exp2开头的exp1

# (?<=exp2)exp1:查找 exp2 后面的 exp1。
# exp1(?<=exp2):查找exp2结束的exp1

# (?<!exp2)exp1:查找前面不是 exp2 的 exp1。
# exp1(?<!exp2):查找不是 exp2结尾 的 exp1。

# 说明:
# 1、(?=exp2)可以理解为把边界定位到exp2前面,然后根据exp1所在的位置决定在边界前或边界后查找。
print(re.findall(r'.+(?=aa)', '123aa456aa789aa258'))  # ['123aa456aa789'] 因为前面的exp1(.+)是贪婪模式,所以查找最后面的aa
print(re.findall(r'.{3}(?=aa)', '123aa456aa789aa258'))  # ['123', '456', '789']
# r'(?=aa).+' 相当于 r'aa.+'
# r'(?=aa).{3}' 相当于 r'aa.{1}'

# 不定长边界:边界前有限定符* + 这里要注意的是边界都是非贪婪的,懒惰模式
# 所以:下面的(?=.+aa)等同于(?=.+?aa)
print(re.findall(r'.+(?=.+aa)', '123aa456aa789aa258'))  # ['123aa456aa78']  注意,没输出9,(?=.+aa), .+只占用一个字符,所匹配的其实是9aa
print(re.findall(r'.{3}(?=.+aa)', '123aa456aa789aa258'))  # ['123', 'aa4', '56a', 'a78'] (?=.+aa)边界分别对应:aa456aa,56aa,a789aa,9aa



# exp1(?!exp2):查找后面不是 exp2 的 exp1。
# (?!exp2)exp1:查找不是exp2开头的exp1
print(re.findall(r'.+(?!aa)', '123aa456aa789aa258'))  # ['123aa456aa789aa258'] 因为前面的exp1(.+)是贪婪模式,258后面没有aa
print(re.findall(r'.{3}(?!aa)', '123aa456aa789aa258'))  # ['23a', 'a45', '6aa', '89a', 'a25']
# 下面两行查找不是aa开头的字符串
# print(re.findall(r'(?!aa).+', '123aa456aa789aa258'))  # ['123aa456aa789aa258']
# print(re.findall(r'(?!aa).{3}', '123aa456aa789aa258'))  # ['123','a45', '6aa', '789','a25]  从边界位置开始查找3个字符

print(re.findall(r'.+(?!.+aa)', '123aa456aa789aa258'))  # ['123aa456aa789aa258']
print(re.findall(r'.{3}(?!.+aa)', '123aa456aa789aa258'))  # ['789', 'aa2']


# (?<=exp2)exp1:查找 exp2 后面的 exp1。
# exp1(?<=exp2):查找exp2结束的exp1
# 说明:
# 1、(?<=exp2)可以理解为把边界定位到exp2后面,然后根据exp1所在的位置决定在边界前或边界后查找。
print(re.findall(r'.+(?<=aa)', '123aa456aa789aa258'))  # ['123aa456aa789aa']
print(re.findall(r'.{3}(?<=aa)', '123aa456aa789aa258'))  # ['3aa', '6aa', '9aa']
print(re.findall(r'(?<=aa).+', '123aa456aa789aa258'))  # ['456aa789aa258']
print(re.findall(r'(?<=aa).{3}', '123aa456aa789aa258'))  # ['456', '789', '258']


# (?<!exp2)exp1:查找前面不是 exp2 的 exp1。
# exp1(?<!exp2):查找不是 exp2结尾 的 exp1。

# 1、(?<!exp2)可以理解为把边界定位到exp2后面,然后根据exp1所在的位置决定在边界前或边界后查找。
print(re.findall(r'.+(?<!aa)', '123aa456aa789aa258'))  # ['123aa456aa789aa258']
print(re.findall(r'.{3}(?<!aa)', '123aa456aa789aa258'))  # ['123', 'aa4', '56a', 'a78', 'aa2']
print(re.findall(r'(?<!aa).+', '123aa456aa789aa258'))  # ['123aa456aa789aa258']
print(re.findall(r'(?<!aa).{3}', '123aa456aa789aa258'))  # ['123', 'aa4', '56a', 'a78', '9aa']

 例:

import re
# 要示:把,替换成#,但是""双引号里面的,不能替换
str = '111,22434222,"3534533,3fgd3",44ghg44'
pram =r'(".*?),(.*?")'

str = re.sub(pram, '\\1$\\2', str)  # 反向引用()括号里的捕获需要\\双反斜杆
str = re.sub(',', '#' ,str)
str = re.sub('\$', ',' ,str)
print(str) # 111#22434222#"3534533,3fgd3"#44ghg44

 

posted @ 2020-10-08 21:53  老谭爱blog  阅读(168)  评论(0)    收藏  举报