一、正则匹配的基本知识
1.1 正则表达式的基本概念
正则表达式是对字符串进行操作的一种逻辑公式,就是用事先设定好的一些特定字符,及这些字符的组合组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑
正则表达式是用来匹配字符串的一种强大工具。
1.2 正则表达式的基本概念
语法 | 说明 | 表达式实例 | 完整匹配的字符串 |
---|---|---|---|
一般字符 | 匹配本身 | abc | abc |
. | 匹配除换行符以外的所有字符 | a.c | abc |
\ | 转译字符,使用后改变字符原本的意思 | a\.c | a.c |
a\\c | a\c | ||
[…] | 字符集,对应的位置可以是字符集中的任意字符,字符集中的字符可以逐个列出,也可以给出范围,如[abc]或者[a-c],第一个字符^代表取反,如[^abc]代表不是abc的其他字符 | a[bcd]e | abe ace ade |
所有的特殊字符在字符集中都失去其原有的意义,在字符集中如果要使用]、-或^,可以在前面加上反斜杠,或者把]、-放在第一个字符,把^放在非第一个字符。 | |||
\d | 数字:[0-9] | a\dc | a1c |
\D | 非数字:[^\d] | a\Dc | abc |
\s | 空白字符:[空格 \t \r \n \f \v] | a\sc | a c |
\S | 非空白字符:[^\s] | a\Sc | abc |
\S | 非空白字符:[^\s] | a\Sc | abc |
\w | 单词字符:[A-Z a-z 0-9 ] | a\w c | abc |
\W | 非单词字符:[^\w ] | a\W c | a c |
数量词(用在字符或(…)之后) | |||
* | 匹配前一个单词0次或者无限次 | abc* | ab abccc |
+ | 匹配前一个单词1次或者无限次 | abc+ | abc abccc |
? | 匹配前一个单词1次或者0次 | abc? | ab abc |
{m} | 匹配前一个字符m次 | ab(2)c | abbc |
{m,n} | 匹配前一个字符m至n次,m和n可以省略:若省略m,则匹配0-n次;若省略n,则匹配m至无限次 | ab(1,2)c | abc abbc |
*? +? ?? | 使* + ?(m,n)变成非贪婪模式 | 示例将在下文中讲解 | |
边界匹配(不消耗匹配字符串中的字符) | |||
^ | 匹配字符串中的开头,多行匹配中匹配每一行的开头 | ^abc | abc |
$ | 匹配字符串中的结尾,多行匹配中匹配每一行的结尾 | abc$ | abc |
\A | 仅匹配字符串中的开头 | \Aabc | abc |
\Z | 仅匹配字符串中的结尾 | abc\Z | abc |
[\u4e00-\u9fa5]+ | 匹配所有的汉字 | ||
逻辑、分组 | |||
| | |代表匹配表达式中任意一个, 但会优先尝试左边的表达式,一旦匹配成功就跳过右边的表达式 | abc|def | abc def |
(…) | ()代表一个分组,分组做为一个整体进行处理,分组中也可以接量词或者|,但是|仅在该组中有效 | a(123|456)c | a123c a456c |
二、re及相关模块的使用
2.1、re.compile 函数
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法格式为:
pattern : 一个字符串形式的正则表达式
flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:
re.I 忽略大小写
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M 多行模式
re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
re.X 为了增加可读性,忽略空格和 # 后面的注释
flags代表匹配的方式
例1:
import re
pattern = re.compile(r'\d+') # 用于匹配其中的任意数字
m = pattern.findall('one12twothree34four')
m
[‘12’, ‘34’]
import re
pattern = re.compile(r'"the"') # 用于匹配至少一个数字
m = pattern.findall('The fat cat sat on the mat')
print(m)
输出为[ ]
因为正则表达式大小写敏感
正则表达式是大小写敏感的,所以The不会匹配the。
2.2 正则表达式相关搜索函数
①match
从字符串开头开始匹配,匹配成功就返回一个对象,失败就返回None
语法:match(pattern,string)
import re
print(re.match("hello",'hello pc').group())
结果:
hello
②search
函数在字符串中查找模式匹配,只要找到第一个匹配就返回,没有就返回None
语法:search(pattern,string)
例1:
res=re.compile('[a-zA-Z]{3}')
str1='123abc456'
print(re.search(res,str1).group())
结果:
abc
注意:两者区别:match的匹配是从字符串的开头开始匹配,即如果字符串开头不符合正则表达式规律就返回None,search是搜索整个字符串,如果整个字符串中没有符合正则表达规律的就返回None,如果有立刻返回,search只匹配一次,即使字符串中有多个符合标准的字符串也只返回一个(这里要和find区别,find是返回字符串中的符合标准的字符串位置。)
例如:str1=‘123abc456’,str1.find(‘abc’),返回3)。
例如:re.match(“1000”,“lalal1000”)就返回None。
③ findall
匹配符合正则表达式的所有字符
语法:findall(pattern, string ,flags=0)
str1="彭闯"
a=re.findall("pengchuang",str1)
print(a)
#结果:[]
str1='java=1000,python=9090'
info=re.findall(r'\d+',str1)#'\d'代表任意一个数字,'+'可以匹配数字一次或者无限次,只能匹配最前
print(info)
#结果:['1000', '9090']
str1="1 0 0 0 "
info=re.findall(r'\d+',str1)#'\d'代表任意一个数字,'+'可以匹配数字一次或者无限次,只能匹配最前
print(info)
#结果:['1','0','0','0']
pattern=re.compile('[a-zA-Z]+') #匹配任意长度的英文字符
pattern=re.compile('[a-z][a-zA-Z][a-zA-Z]') #匹配3个长度的英文字符
str1='123abc456def789'
result=re.findall(pattern,str1)
print(result)
#结果:['abc', 'def']
pattern=re.compile('a(123|456)c') #匹配3个长度的英文字符
str1='a123ca456c'
result=re.findall(pattern,str1)
print(result)
#结果:['123', '456']
#findall遇见分组会优先返回分组的内容
findall会匹配所有找到的字符,如果找到了会将所有字符返回列表,如果没找到,则会返回[ ]
总结:findall会匹配所有字符,如果找到了会将所有字符返回列表,如果没找到,则会返回[ ]
④ finditer()
finditer(pattern,str):finditer会顺序访问每一个匹配结果。不管正则表达式中是否含有分组。
pattern=re.compile('a(123|456)c') #匹配3个长度的英文字符
str1='a123ca456c'
result=re.finditer(pattern,str1)
for i in result:
print(i.group())
a123c
a456c
for i in result:
print(i.group(1)) #返回每个分组的第二词
1
4
⑤split()
split(patten,string):按照正则匹配的字串将字符串进行分割
str1='我叫pc,大家好'
str1.split(",")
print(re.split('\d+','one1two2three3four4five5'))
print(re.split('\W+','one,two,three,four,five'))
['one', 'two', 'three', 'four', 'five', '']
['one', 'two', 'three', 'four', 'five']
⑥sub(),使用re替换string中每一个匹配的字串后返回替换后的字符串
语法sub(pattern,‘替换符号’,string)
print(re.sub('\d+','-','one1two2three3four4five5'))
#结果:
one-two-three-four-five-
⑦subn(),返回替换次数
print(re.subn('\d+','-','one1two2three3four4five5'))
('one-two-three-four-five-', 5)
⑧引用分组
strs='hello 123,world 321'
pattern=re.compile('(\w+) (\d+)')
for i in pattern.finditer(strs):
print(i)
print(i.group(0))
print(i.group(1))
print(i.group(2))
结果:
hello 123
hello
123
world 321
world
321
这是因为分组以后,group(0)为整个分组的字符串,group(1)为当前分组的第一个字符串。group(2)为当前分组的第二个字符串。
strs='hello 123,world 321'
pattern=re.compile('(\w+) (\d+)')
print(pattern.sub(r'\2 \1',strs)) #颠倒分组
print(pattern.sub(r'\2+++\1',strs)) #颠倒分组
结果:
123 hello,321 world
123+++hello,321+++world
⑨贪婪匹配和非贪婪匹配
贪婪:在整个表达式匹配成功的前提下,尽可能多的匹配
非贪婪:在整个表达式匹配成功的前提下,尽可能少的匹配
例1:
1、
str1='aaa<p>hello<\p>bbb<p>world<\p>ccc'
pattern=re.compile(r'<p>\w+<\\p>') #匹配两个p之间的字符1个或者无限个
print(pattern.findall(str1))
2、
str1='aaa<p>hello<\p>bbb<p>world<\p>ccc'
pattern=re.compile(r'<p>.*<\\p>') #匹配两个p之间的字符1个或者无限个
print(pattern.findall(str1))
结果:
['<p>hello<\\p>', '<p>world<\\p>']
['<p>hello<\\p>bbb<p>world<\\p>']
2、 元字符
正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思,他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍:
元字符 描述
. 句号匹配任意单个字符除了换行符。
[ ] 字符种类。匹配方括号内的任意字符。
[^ ] 否定的字符种类。匹配除了方括号里的任意字符
* 匹配>=0个重复的在*号之前的字符。
+ 匹配>=1个重复的+号前的字符。
? 标记?之前的字符为可选.
{n,m} 匹配num个大括号之前的字符 (n <= num <= m).
(xyz) 字符集,匹配与 xyz 完全相等的字符串.
| 或运算符,匹配符号前或后的字符.
\ 转义字符,用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \ |
^ 从开始行开始匹配.
$ 从末端开始匹配.
2.1 点运算符 .
.是元字符中最简单的例子。 .匹配任意单个字符,但不匹配换行符。 例如,表达式.ar匹配一个任意字符后面跟着是a和r的字符串。
import re
pattern = re.compile(r'.ar') #匹配以ar结尾的单词
m = pattern.findall('The car parked in the garage.')
print(m)
[‘car’, ‘par’, ‘gar’]
2.2 字符集[ ]
字符集也叫做字符类。 方括号用来指定一个字符集。 在方括号中使用连字符来指定字符集的范围。 在方括号中的字符集不关心顺序。 例如,表达式[Tt]he 匹配 the 和 The。
例1:
import re
pattern = re.compile(r'[T|t]he') # 匹配以The和the的单词
m = pattern.findall('The car parked in the garage.')
print(m)
[‘The’, ‘the’]
例2:
方括号的句号就表示句号。 表达式 ar[.] 匹配 ar.字符串
"ar[.]" => A garage is a good place to park a car.
匹配 ar.
2.2.1 否定字符集
例3:
一般来说 ^ 表示一个字符串的开头,但它用在一个方括号的开头的时候,它表示这个字符集是否定的。
例如,表达式[^c]ar 匹配一个后面跟着ar的除了c的任意字符。
#匹配不以c开头但是以ar结尾的单词
"[^c]ar" => The car parked in the garage.
2.3 重复次数
后面跟着元字符 +,* or ? 的,用来指定匹配子模式的次数。 这些元字符在不同的情况下有着不同的意思。
2.3.1 * 号
*号匹配 在*之前的字符出现大于等于0次。
例如,表达式 a* 匹配0或更多个以a开头的字符。表达式[a-z]* 匹配一个行中所有包含小写字母的字符串。
"[a-z]*" => The car parked in the garage
结果
[‘he car parked in the garage’]
例2:
*字符和.字符搭配可以匹配所有的字符.*。 *和表示匹配空格的符号\s连起来用,如表达式\s*cat\s*匹配0或更多个空格开头和0或更多个空格结尾的cat字符串。
"\s*cat\s*" => The fat cat sat on the concatenation.
pattern = re.compile(r'\s*cat\s*') # 匹配以The和the的单词
m = pattern.findall('The fat cat sat on the concatenation')
print(m)
[' cat ', 'cat']
例3:匹配单词中包含cat的字符串
例4:
pattern = re.compile(r'\w*cat\w*') # 匹配包含cat这个单词的字符
m = pattern.findall('The fat cat sat on the concatenation')
print(m)
[‘cat’, ‘concatenation’]
2.3.2 + 号
+号匹配+号之前的字符出现 >=1 次。 例如表达式c.+t 匹配以首字母c开头以t结尾,中间跟着至少一个字符的字符串。
"c.+t" => The fat cat sat on the mat.
匹配到的字符串
[‘cat sat on the mat’]
注意这里我们一定是贪心匹配,即匹配最多的字符串
2.3.3 、? 号
在正则表达式中元字符 ? 标记在符号前面的字符为可选,即出现 0 或 1 次。 例如,表达式 [T]?he 匹配字符串 he 和 The。
"[T]he" => The car is parked in the garage.
结果:
[‘The’]
2.4、 {} 号
在正则表达式中 {} 是一个量词,常用来一个或一组字符可以重复出现的次数。 例如, 表达式 [0-9]{2,3} 匹配最少 2 位最多 3 位 0~9 的数字。
例1:
"[0-9]{2,3}" => The number was 9.9997 but we rounded it off to 10.0.
pattern = re.compile(r'[0-9]{2,3}')
m = pattern.findall('The number was 9.9997 but we rounded it off to 10.0.')
print(m)
结果:
[‘999’, ‘10’]
例2:
'''
匹配11位手机号码
'''
pattern = re.compile(r'[0-9]{11}')
m = pattern.findall('我今年11岁,我的手机号码是17798558749')
print(m)
[‘17798558749’]
例3:
我们可以省略第二个参数。 例如,[0-9]{2,} 匹配至少两位 0~9 的数字。
"[0-9]{2,}" => The number was 9.9997 but we rounded it off to 10.0.
结果:
[‘999’, ‘10’]
2.5、 […] 特征标群
特征标群是一组写在 […]中的子模式。表示取出方括号中的字符
例1:[c|g|p]ar 匹配 car 或 gar 或 par.
"[c|g|p]ar" => The car is parked in the garage.
['car','par','gar']
2.6 | 或运算符
或运算符就表示或,用作判断条件。
例1(T|t)he|car 匹配 (T|t)he 或 car。
"(T|t)he|car" => The car is parked in the garage.
2.7 转码特殊字符
反斜线 \ 在表达式中用于转码紧跟其后的字符。用于指定 { } [ ] / \ + * . $ ^ | ? 这些特殊字符。如果想要匹配这些特殊字符则要在其前面加上反斜线 \。
例1 匹配句子中所有以'fat','cat','mat',或者+'.'结尾的符号
"[f|c|m]at\.?" => The fat cat sat on the mat.
pattern = re.compile("[f|c|m]at\.?")
m = pattern.findall('The fat cat sat on the mat.')
print(m)
[‘fat’, ‘cat’, ‘mat.’]
如果我不加.?则最后一个只会出现cat
例2
匹配以fat,cat,mat+.结尾的字符
pattern = re.compile("[f|c|m]at\.")
m = pattern.findall('The fat cat sat on the mat.')
print(m)
[‘mat.’]
例3:
匹配以fat,cat,mat+.结尾的字符
pattern = re.compile("[f|c|m]at\.")
m = pattern.findall('The fat cat sat on the mat.')
print(m)
例4:
2.8 锚点
在正则表达式中,想要匹配指定开头或结尾的字符串就要使用到锚点。^ 指定开头,$ 指定结尾。
2.8.1 ^ 号
^ 用来检查匹配的字符串是否在所匹配字符串的开头。
例如,在 abc 中使用表达式 ^a 会得到结果 a。但如果使用 ^b 将匹配不到任何结果。因为在字符串 abc 中并不是以 b 开头。
例1:
匹配以 The 或 the 开头的字符串。
pattern = re.compile('^[T|t]he')
m = pattern.findall('The car is parked in the garage.')
print(m)
[‘The’]
因为正则匹配时将后面的字符串看做一整个字符串,所以美亚匹配到the
2.8.2 $ 号
同理于 ^ 号,$ 号用来匹配字符是否是最后一个。
'''
例10
匹配以 at.结尾的字符串。
'''
pattern = re.compile('at\.')
m = pattern.findall('The fat cat. sat. on the mat.')
print(m)
['at.', 'at.', 'at.']
pattern = re.compile('at\.$')
m = pattern.findall('The fat cat. sat. on the mat.')
print(m)
['at.']
可以看出加上$代表以at.结尾且是最后一个字符的字符串
3. 简写字符集
正则表达式提供一些常用的字符集简写。如下:
简写 描述
. 除换行符外的所有字符
\w 匹配所有字母数字,等同于 [a-zA-Z0-9_]
\W 匹配所有非字母数字,即符号,等同于: [^\w]
\d 匹配数字: [0-9]
\D 匹配非数字: [^\d]
\s 匹配所有空格字符,等同于: [\t\n\f\r\p{Z}]
\S 匹配所有非空格字符: [^\s]
\f 匹配一个换页符
\n 匹配一个换行符
\r 匹配一个回车符
\t 匹配一个制表符
\v 匹配一个垂直制表符
\p 匹配 CR/LF(等同于 \r\n),用来匹配 DOS 行终止符
\b:表示字母数字与非字母数字的边界
\B:非字母数字与非字母数字的边界。
补充:
[\u4e00-\u9fa5]+ 匹配所有的汉字
4、 前后预查
零宽度断言如下:
符号 描述
?= 正先行断言-存在
?! 负先行断言-排除
?<= 正后发断言-存在
?<! 负后发断言-排除
4.1、 ?=… 正先行断言
1、 什么是零宽断言
有时候在使用正则表达式做匹配的时候,我们希望匹配一个字符串,这个字符串的前面或后面需要是特定的内容,但我们又不想要前面或后面的这个特定的内容,这时候就需要零宽断言的帮助了。所谓零宽断言,简单来说就是匹配一个位置,这个位置满足某个正则,但是不纳入匹配结果的,所以叫“零宽”,而且这个位置的前面或后面需要满足某种正则。
2、四种正则表达
(1)正预测先行:简称正向先行断言,语法:(?=exp),它断言此位置的后面能匹配表达式exp,但不包含此位置;如:a(?=\d),返回匹配字符串中以数字为结尾的a字符。
(2)正回顾后发:简称正向后发断言,语法:(?<=exp),它断言此位置的前面能匹配表达式exp;
如:(?<=\d)a,返回匹配字符串中以数字为开头的a字符。
(3)负预测先行:简称反向先行断言,语法:(?!exp),它断言此位置的后面不能匹配表达式exp;
如:a(?!\d),返回不匹配字符串中以数字结尾的a字符。
(4)负回顾后发:简称反向后发断言,语法:(?<!exp),它断言此位置的前面不能匹配表达式exp;
如:a(?<!exp)a,返回不匹配字符串中以数字开头的a字符。
4.1、正向先行(?=exp):获取字符串中以ing结尾的字符:
4.2
正向后发(?<=exp):获取字符串中以do开头的单词后半部分:
s='doing done do todo'
pattern = re.compile(r'(?<=\bdo)\w+\b')
m = pattern.findall(s)
print(m)
[‘ing’, ‘ne’]
4.3、反向先行(?!exp):匹配出字符串中不是以ing结尾的单词:
s='do run going'
pattern = re.compile(r'\b\w+\W(?!ing)')
m = pattern.findall(s)
print(m)
['do ', 'run ']
4.4、反向后发(?<!exp):匹配字符串中不以do开头的单词:
5. 标志
5.1 惰性匹配
python的正则表达一般都是贪心算法,即匹配出符合要求的最大字符串
s='The fat cat sat on the mat.'
pattern = re.compile(r'.*at')
m = pattern.findall(s)
print(m)
[‘The fat cat sat on the mat’]
如果我们要非惰性匹配,则需要
s='The fat cat sat on the mat.'
pattern = re.compile('.*?at')
m = pattern.findall(s)
print(m)
[‘The fat’, ’ cat’, ’ sat’, ’ on the mat’]
我们发现对于fat这个词来说,满足以at结尾,且前面有字符,
cat同样满足,且The fat cat也满足,我们选择一个最小的。
sat也满足,且sat在满足的情况下最小
mat也满足,且满足的情况下最小