一、概述
1)正则表达式是一个特殊的字符序列,能帮助你方便的检查一个字符串是否与某模式匹配
2)python1.5开始增加 re 模块,提供Perl风格的正则表达式模式
3)compile函数根据一个模式字符串和可选的正则表达式参数生成一个正则表达式对象,该对象拥有一系列方法用于正则表达式匹配和替换
4)re模块提供了与正则表达式对象方法功能完全一样的函数,这些函数使用一个模式字符串作为它们的第一个参数
二、正则表达式教程
1、简介
1)正则表达式(Regular Expression)是一种文本模式,包括普通字符和特殊字符(元字符)
2)正则表达式的功能比通配符*和?强得多,而且更加灵活
3)正则表达式描述了一种字串的匹配模式(pattern),用来检查一个字符串示符包含有某种子串,将匹配的子串替换或者取出
4)一个简单的示例
^[0-9]+abc$
- ^:匹配字符串的开始位置
- [0-9]:匹配一个数字
- [0-9]+:匹配多个数字
- +:匹配一个或多个
- abc$:匹配字母abc,并以abc结尾
- $:匹配字符串的输入结束位置
4)如果我们要求用户输入的字符串只能包含小写字母、数字、下划线(_)和连字符(-),且长度在3-15,则可用以下正则表达式来约束
^[a-z0-9_-]{3,15}$
- ^:开始标记
- [a-z0-9_-]:字符串可由里边的字符构成
- {3,15}:长度在3-15之间
- $:结束标记
- 此正则表达式能匹配"123abc"、"asdfg"这样的字符串,但是不能匹配"asd%"、"as"这样的字符串
2、为什么使用正则表达式
典型的搜索替换模式可以适用于静态文本,对于动态文本来说使用较为复杂,通过使用正则表达式,可以达到以目的:
- 可以测试字符串内的模式(测试输入字符串,可以测试字符串是否符合电话号码或信用卡模式,称为数据验证)
- 替换文本(可以使用正则表达式来识别文档中的的特定文本完全删除该文本或用其他文本替换它)
- 基于模式匹配从字符串中提取字串(可以查找文档内或输入域内的特定文本)
3、正则表达式的应用领域
vi、visual C++、awk、sed、delphy、python、Java、JavaScript、PHP、Perl、C#
4、语法
1)正则表达式的组件可以是单个字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合
2)正则表达式是由普通字符和特殊字符(元字符)组成的文字模式
3)普通字符:所有大写字母、小写字母、数字、标点符号、其它符号
4)非打印字符也是普通字符的一部分:
- \cX:匹配由X指明的控制字符,\cM匹配的是Ctrl+M或回车符。X的值必须是A-Z或a-z之一,否则c视为'c'字符
- \f:匹配一个换页符,等价于\x0c和\cL
- \n:匹配一个换行符。等价于 \x0a 和 \cJ
- \r:匹配一个回车符。等价于 \x0d 和 \cM
- \s:匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符
- \S:匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
- \t:匹配一个制表符。等价于 \x09 和 \cI
- \v:匹配一个垂直制表符。等价于 \x0b 和 \cK
5)特殊字符(元字符)
比如a*b中的*,就是任何字符串的意思,如果要查找字符串中的*,就必须对*进行转义,在其前边加转义字符\,变为a\*b匹配"a*b"
- $:匹配字符串的结尾位置
- ():标记一个子表达式的开始和结束位置
- *:匹配前面的子表达式零次或多次
- +:匹配前面子表达式一次或多次
- ?:匹配前面的子表达式零次或一次
- .:匹配除换行符\n之外的任何单字符
- [:标记一个中括号表达式的开始
- {:标记限定符表达式的开始
- |:指明两项之间的选择
- \:将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n','\n' 匹配换行符,序列 '\\' 匹配 "\",而 '\(' 则匹配 "("
- ^:匹配输入字符串的开始位置。除非在方括号表达式中使用,此时它表示不接受该字符集合
6)限定符
限定符限定了它前边的组建必须出现多少次
- *:零次或多次
- +:一次或多次
- ?:零或一次
- {n}:n次
- {n,}:最少n次
- {n,m}:n-m次
7)一些例子
匹配章节号时,因为需要处理两位、三位的章节号,所以可以使用下面的正则表达式来匹配
chapter [1-9][0-9]*
如果知道章节号被限制到99章,可以用下面的正则表达式匹配
chapter [0-9]{1,2}
但是上述表达式的缺点是将chapter 0也匹配了,要匹配只有两位数的章节号更合适的是下面的正则表达式
chapter [1-9][0-9]?
*和+都是贪婪的,它们会尽可能多的匹配文字,只要在它们的后边加上?就能实现最小匹配
在下面的字符串匹配中
<H1>这是一个字符串匹配的例子<H1>
贪婪的情况下使用<.*>会匹配到整个字符串,但是使用<.*?>只会匹配到<H1>(待验证是前边的H1还是后边的H1)
8)定位符
定位符能够将正则表达式定位在某一具体位置(行首/行尾/单词内/单词开头/单词结尾)
- ^:匹配输入字符串的开始位置,若设置了 RegExp 对象(正则表达式对象)的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配
- $:匹配输入字符串的结束位置,若设置了 RegExp 对象(正则表达式对象)的 Multiline 属性,^ 还会与 \n 或 \r 之前的位置匹配
- \b:匹配一个单词边界,即字与空格间的位置
- \B:非单词边界匹配
9)一些例子
下面的正则表达式匹配一个章节标题,并只包含"chapter"和两位数的数字
^chapter [1-9][0-9]?$ 或 ^chapter [1-9][0-9]{0,1}$
单词边界是单词和空格之间的位置。非单词边界是任何其他位置
下面的两个表达式分别匹配单词chapter的前三个字符和后三个字符,因为这三个字符出现在单词边界之后和之前
\bcha 和 ter\b
下面的表达式匹配chapter中的"apt",但是不能匹配 aptitude 中的 apt
\Bapt
10)选择
将所有选择用圆括号括起来,选项中间用 | 隔开
但是用圆括号有一个副作用,它会将相关匹配缓存到一个临时缓存区,此时可在第一个选项前放?:来消除这种副作用
其中 ?: 是非捕获元之一,还有两个非捕获元是 ?= 和 ?!,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串
例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式
5、元字符的补充
(pattern) | 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。 |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。 |
(?=pattern) | 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,"Windows(?=95|98|NT|2000)"能匹配"Windows2000"中的"Windows",但不能匹配"Windows3.1"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)"能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?<=pattern) | 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,"(?<=95|98|NT|2000)Windows "能匹配"2000Windows "中的"Windows ",但不能匹配"3.1Windows "中的"Windows "。 |
(?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如"(?<!95|98|NT|2000)Windows "能匹配"3.1Windows "中的"Windows ",但不能匹配"2000Windows "中的"Windows "。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\w | 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'。 |
\W | 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'。 |
\xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。 |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 |
\nm | 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 |
\nml | 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
6、运算符优先级
从高到低:
- \:转义字符
- (),(?:),(?=),[]:圆括号和方括号
- *,?,+,{n},{n,},{n,m}:限定符
- ^,$,\任何元字符,任何字符:定点位和序列(位置和顺序)
- |:替换(或操作)
7、匹配规则
1)基本模式匹配
模式是正则表达式的最基本元素,它们是一组描述字符串特征的字符
模式可以由普通的字符串组成,也可以用特殊的字符表示一个范围内的字符、重复出现、表示上下文等
比如:^once匹配以once开头的字符串,once匹配任何包含once的字符串
如果要检查一个字符串是否以制表符开头可以使用 ^\t
2)字符簇
在INTERNET程序中,正则表达式经常用来验证用户的输入格式,用普通的基于字面的字符是不够的,要用到字符簇
比如,建立一个包含所有元音的字符簇[AaEeIiOoUu]——与任何的元音字符匹配,但是只能表示一个字符
用连字符号可以表示一个字符的范围[a-z0-9\.\-]——匹配所有小写字母、数字、句号和减号
[ \f\r\t\n]——匹配所有空白字符
^[a-z][0-9]$——匹配第一个字符为小写字母,第二个字符为0-9的字符串
^[^0-9][0-9]$——要求匹配的第一个字符不能是数字
[^\\\/\^]——除了(\)(/)(^)以外的所有字符
当在一组方括号里使用^,表示“非”的意思
特殊字符“.”(点)用来表示除新行以外的所有字符
比如,^.5$与任何一个两字符的、以数字5结尾、以其它非“新行”字符开头的字符串匹配
模式“.”匹配任何字符串,除了空串和只包括一个“新行”的字符串
3)几个例子
^[a-zA-Z0-9_]+$:所有包含一个字符以上的字母、数字、下划线的字符串
^[1-9][0-9]*$:所有的正整数
^\-?[0-9]+$:所有的整数
^[-]?[0-9]+(\.[0-9]+)?$:所有的浮点数
三、re.match函数
1、说明
尝试从字符串的起始位置匹配一个模式,如果不是起始位置成功匹配的话,re.match就返回none,匹配成功就返回一个re对象
re.RegexObject为正则表达式对象
re.MatchObject是匹配结果对象
2、语法
re.match(pattern, string, flags)
参数说明:
pattern:匹配的正则表达式
string:要匹配的字符串
flags:标志位,用于控制正则表达式的匹配方式(可选标志,如是否区分大小写、多行匹配等)
3、group(num)和groups()
group(num):匹配整个表达式的字符串,group可以依次输入多个组号(在正则表达式中有小括号括起来的为一组),在这种情况下它将返回第num组所对应的值
groups():返回一个包含所有小组字符串的元组,从一到所包含的小组号
4、实例
>>> import re
>>> line = "Cats are smarter than dogs"
>>> matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
>>> matchObj
<re.Match object; span=(0, 26), match='Cats are smarter than dogs'>
>>> matchObj.group()
'Cats are smarter than dogs'
>>> matchObj.group(1)
'Cats'
>>> matchObj.group(2)
'smarter'
>>> matchObj.groups()
('Cats', 'smarter')
>>> type(matchObj.groups())
<class 'tuple'>
四、re.search方法
1、说明
re.search方法扫描整个字符串并返回第一个成功的匹配,否则返回None
2、语法
re.search(pattern, string, flag=0)
pattern:匹配的正则表达式
string:要匹配的字符串
flag:标志位
3、group(num)和groups()
group(num):匹配整个表达式的字符串,group可以依次输入多个组号(在正则表达式中有小括号括起来的为一组),在这种情况下它将返回第num组所对应的值
groups():返回一个包含所有小组字符串的元组,从一到所包含的小组号
4、实例
>>> line = "Cats are smarter than dogs"
>>> searchObj = re.search(r'(.*) are (.*?) .*', line, re.M|re.I)
>>> searchObj
<re.Match object; span=(0, 26), match='Cats are smarter than dogs'>
>>> searchObj.groups()
('Cats', 'smarter')
五、re.match和re.search方法对比
re.match只在字符串开始进行匹配,如果开始不匹配,则匹配失败,函数返回None
re.search匹配整个字符串,直到找到一个匹配
对比实例:
#test.py
import re
matchObj = re.match("com","www.baidu.com")
if matchObj:
print(matchObj)
else:
print("not found")
searchObj = re.search("com","www.baidu.com")
if searchObj:
print(searchObj)
else:
print("not found")
执行结果:
六、检索和替换
1、说明
re模块提供了re.sub方法用于替换字符串中的匹配项
2、语法
re.sub(pattern, repl, string, count, flags)
pattern:正则中的模式字符串
repl:替换的字符串,也可为一个函数
string:要被查找替换的原始字符串
count:模式匹配后替换的最大次数,默认0表示替换所有匹配(可选参数)
flags:编译时用的匹配模式,数字形式(可选参数)
3、实例:
#test.py
import re
phone = "2004-643-245 #这是一个电话号码"
print("原电话号码:", phone)
#删除注释
num = re.sub(r'#.*', '', phone)
print("删除注释后的电话号码:", num)
#仅保留数字
num = re.sub(r'[^0-9]', '', num)
print("仅保留数字的电话号码:", num)
执行结果:
4、当repl为函数时
匹配到的re对象将会参数的形式传入repl函数,在repl函数中可对re对象进行任何操作最后返回要替换的字符串
#test.py
import re
#将匹配的数字乘以2
def double(i):
value = int(i.group())
return str(value*2)
line = "ABC123DEF567"
result = re.sub(r'(\d+)', double, line)
print(result)
执行结果:
七、compile函数
1、说明
re模块提供compile函数来生成一个正则表达式(pattern)对象,提供match()和search()两个函数使用
2、语法
re.compile(pattern[, flags])
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 为了增加可读性,忽略空格和' # '后面的注释
3、实例1
>>> pattern = re.compile(r'\d+') #匹配至少一个数字的正则对象
>>> m = pattern.match("abc123def456g7") #查找头部,没有匹配
>>> print(m)
None
>>> m = pattern.match("abc123def456g7", 3, 10) #从1的位置开始匹配
>>> print(m)
<re.Match object; span=(3, 6), match='123'>
>>> m.group(0) #0可省略
'123'
>>> m.start()
3
>>> m.end()
6
>>> m.span()
(3, 6)
在上面,当匹配成功时返回一个 Match 对象,其中:
group([group1, …])
方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用group()
或group(0)
;start([group])
方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;end([group])
方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;span([group])
方法返回(start(group), end(group))
。
4、实例2
>>> import re
>>> pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) #re.I表示忽略大小写
>>> m = pattern.match("My name is ZhouLe")
>>> print(m)
<re.Match object; span=(0, 7), match='My name'>
>>> m.groups()
('My', 'name')
>>> m.span(1)
(0, 2)
>>> m.span(2)
(3, 7)
>>> m.group(3)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
m.group(3)
IndexError: no such group
>>>
八、findall函数
1、说明
在字符串中找到正则表达式匹配的所有子串并返回一个列表,如果没有找到匹配的,则返回空列表
match和search只匹配一次,findall匹配所有
2、语法
pattern.findall(string[, pos[, endpos]])
string:待匹配字符串
pos:可选参数,指定字符串的起始位置,默认为0
endpos:可选参数,指定字符串的结束位置,默认为字符串长度
3、实例
>>> pattern = re.compile(r'(\d+)')
>>> m = pattern.findall("123asd456fgh7j")
>>> print(m)
['123', '456', '7']
九、re.finditer
1、说明
功能与findall类似,不同的地方在于,finditer将匹配结果以迭代器的形式返回
2、语法
re.finditer(pattern, string, flags = 0)
pattern:匹配的正则表达式
string:要匹配的字符串
flags:标志位
3、实例
#test.py
import re
it = re.finditer(r'\d+', "asd123fgh456j7")
for i in it:
print(i)
执行结果:
十、re.split
1、说明
能够按照匹配的子串将字符串分割后返回列表
2、语法
re.split(pattern, string, [maxsplit = 0, flags = 0])
pattern:匹配的正则表达式
string:待匹配的字符串
maxsplit:最大分隔次数,默认为0(不限次数)
flags:标志位
3、实例
#test.py
import re
line = "0one1two2three34four"
result = re.split(r'\d', line, 3)
print(result)
执行结果:
十一、正则表达式修饰符-可选标志
多个标志位可用按位OR(|)它们来指定,如re.I|re.M被设置成 I 和 M 标志
- re.I:使用匹配对大小写不敏感
- re.L:做本地化识别(local-aware)匹配
- re.M:多行匹配,影响^和$
- re.S:使.匹配换行在内的所有字符
- re.U:根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B
- re.X:这个选项忽略规则表达式中的空白和注释,并允许使用 ’#’ 来引导一个注释。这样可以让你把规则写得更美观些
十二、总结
字母和数字表示它们自身
多数字母和数字前边加一个反斜杠会有不同的含义
标点符号只有被转义时才表示它们自身,否则它们表示特殊的含义
反斜杠本身需要反斜杠转义
由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们
模式元素(如 r'\t',等价于 \\t )匹配相应的特殊字符。