我们先介绍一下re模块下的方法,这个是我们拿来就可以用的,当然前提是知道一点正则表达式,如果对正则表达式完全不了解,可以先看一下后面正则表达式部分。
match方法从字符串的起始位置匹配一个模式,如果没有匹配成功match就返回None
re.match(pattern, string, flags=0)pattern:正则表达式 string:待匹配的字符串 flags:匹配模式(是否区分大小写、单行匹配还是多行匹配)
match返回的是一个re.Match对象,后面会详细介绍Match中的方法。
import re content = "Cats are smarter than dogs" # 第一个参数是正则表达式,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主要是用于捕获分组,所以尽量使用分组模式,不然匹配了也无法获取结果,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: 正则表达式 repl: 替换的字符串,可以是函数 string: 要被查找替换的字符串 count: 模式匹配后替换的最大次数,默认0表示替换所有的匹配,可选 flags: 可选参数,匹配模式,默认为0
替换和谐字符:
import re content = "do something fuck you" rs = re.sub(r'fuck', "*", content) print(rs)非常简单,把fuck替换为*
现在需求变了,我们需要被屏蔽的单词有几个字符,就替换为几个*,怎么处理?
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)替换的字符串可以使用函数,我们在函数中就可以非常容易的计算了
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
re.findall(pattern, string, flags=0)pattern:正则表达式 string: 待匹配的字符串 flags: 可选参数,匹配模式,默认为0
import re content = '+123abc456*def789ghi' reg = r"\d+" rs = re.findall(reg, content) # ['123', '456', '789'] print(rs)findall有一个让人狂躁的地方是,如果正则表达式中有分组,就只会返回分组中的匹配。
import re content = '+123abc456*def789ghi' reg = r"\d+([a-z]+)" rs = re.findall(reg, content) # ['abc', 'ghi'] print(rs)在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回
re.finditer(pattern, string, flags=0)pattern:正则表达式 string: 待匹配的字符串 flags: 可选参数,匹配模式,默认为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)编译正则表达式,生成一个正则表达式Pattern对象,前面的方法都会先调用这个方法得到一个Pattern对象,然后再使用Pattern对象的同名方法。
接下来,我们马上就会介绍一下Pattern对象。
re.compile(pattern, flags=0)Pattern对象是一个编译好的正则表达式,Pattern不能直接实例化,必须使用re.compile()进行构造。
前面re模块中介绍的方法对于Pattern都适用,只是少了pattern参数
其实很简单,re模块中的方法使用pattern参数,通过re.compile方法构造了一个Pattern对象
Match对象是一次匹配的结果,包含此次匹配的信息,可以使用Match提供的属性或方法来获取这些信息。
感觉Match的属性稍微有些鸡肋。
注意:无参数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对象中的方法来获取匹配的
最常用的就是.匹配任意字符,a.b就可以匹配abb、acb、adb、a+b、a8b等等
?表示最多匹配一次:abb?可以匹配ab、abb,但是不能匹配abbabb,因为?只是指前一个片段
+表示至少匹配一次:abb+可以匹配abb、abbb、abbbb等等,当时不能匹配ab
* 表示0到多次: abb*可以匹配ab、abb、abbb、abbbb等等
[]中有一组字符,字符间的关系是或
分组最重要的一个作用就是可以回溯,就是引用已经匹配的模式。
思考:html中如何匹配全部h标签?
reg = '<[hH][1-6]>.*?</[hH][1-6]>'很多朋友可能会写出类似于上面的表达式,有什么问题吗?
看下面的例子:
import re content = ''' <html> <body> <H1>first</h1> <p>p tag</p> <h2>h2</h2> <h3>非法标签</h4> </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]>'会把’
我们可以通过分组,然后引用分组来解决这个问题。
reg1 = '<[hH]([1-6])>.*?</[hH]\1>' reg2 = '((<[hH]([1-6])>).*?</[hH]\3>)'因为如果有分组,findall之后打印出匹配分组,所以我们使用reg2这个正则表达式。
为什么是\3呢?
因为根据从左到右,从外到里的原则,我们知道([1-6])是第3个分组。
如果觉得不想去数,或者怕数错,那么就可以使用别名的方式。
reg = '((<[hH](?P<num>[1-6])>).*?</[hH](?P=num)>)'前后匹配也是一个非要有用的功能,它的一个重要特性是不消费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) # 向后匹配,匹配$后面的数字,不消费$ 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)re.M是多行匹配模式:
^可以匹配字符串开头,也可以匹配行的开头,字符串中换行符\n之后的位置$可以匹配字符串结尾,也可以匹配行的结尾,字符串中换行符\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的时候,只需要考虑正则表达式中有没有^和$,如果没有肯定是不需要的,如果有,那么思考是否需要匹配\n(换行符)前后的位置,需要则加上re.M。
re.L和re.U比较不好理解,2.x和3.x的变化也很大,基本用不上,有兴趣可以看一下文档。
re