1. 基本语法
正则表达式也称规则表达式(regular expression,RE),简称正则,可以匹配符合指定模式的文本。
1.1 匹配字符
大多数字符只匹配自己,这些字符称为普通字符。有少量字符却不能匹配自己,它们表示特殊的含义,称为元字符,如果要匹配元字符本身,需要在左侧添加反斜杠(\)进行转义,变成普通字符。
. ^ $ * + ? { } [ ] \ | ( ) \. \^ \$ \* \+ \? \{ \} \[ \] \\ \| \( \)
反斜杠(\)还有其它功能,如以下三种。
定义非打印字符
\cx
:匹配由x指明的控制字符。\f
:匹配一个换页符。等价于\x0c
和\cL
。\n
:匹配一个换行符。等价于\x0a
和\cJ
。\r
:匹配一个回车符。等价于\x0d
和\cM
。\s
:匹配任何空白符,包括空格、制表符、换页符等。等价于[\f\n\r\t\v]
。\S
:匹配任何非空白符。等价于[^\f\n\r\t\v]
。\t
:匹配一个制表符。等价于\x09
和\cI
。
\v
:匹配一个垂直制表符。等价于\x0b
和\cK
。
预定义字符
\d
:匹配一个数字字符。等价于[0-9]
。\D
:匹配一个非数字字符。等价于[^0-9]
。\s
:匹配任何空白符,包括空格、制表符、换页符等。等价于[\f\n\r\t\v]
。\S
:匹配任何非空白符。等价于[^\f\n\r\t\v]
。\w
:匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]
。
\W
:匹配任何非单词字符。等价于[^A-Za-z0-9_]
。
定义断言
\b
:单词定界符。\B
:非单词定界符。\A
:字符串的开始位置,不受多行匹配模式的影响。\Z
:字符串的结束位置,不受多行匹配模式的影响。
1.2 字符类
定义字符类
字符类也称字符集,表示匹配字符集中任意一个字符。使用元字符’[‘和’]'可以定义字符类,例如,[set]可以匹配s、e、t字符集中的任意一个字母。
import re # 导入正则表达式模块 pattern = '[-\[\]^.*]' # 定义特殊字符集 str1 = "[]-\.*^" # 定义字符串 print(re.findall(pattern,str1)) # 输出 ['[', ']', '-', '.', '*', '^']
定义字符范围
使用一个范围表示一组字符,即给出两个字符,并用(-)标记将它们分开,它表示一个连续的、相同系列的字符集。连字符左侧字符为范围起点,连字符右侧字符为范围终点。如[a-c]可以匹配字符a、b、c,它与[abc]功能相同。
字符范围都是根据匹配模式指定的字符编码表中的位置来确定的。
pattern = '[a-z]' # 匹配任意一个小写字母 pattern = '[A-Z]' # 匹配任意一个大写字母 pattern = '[0-9]' # 匹配任意一个数字 pattern = '[\u4e00-\u9fa5]' # 匹配中文字符 pattern = '[\x00-\xff]' # 匹配单字节字符
定义排除范围
如果匹配字符类中未列出的字符,可以包含一个元字符’’,并作为字符类的**第一个字符**。如[0]将匹配除0以外的任何字符。但是如果’^'在字符类的其他位置,则没有特殊含义。
pattern = '[^0-9]' # 匹配任意一个非数字的字符 pattern = '[\x00-\xff]' # 匹配双字节字符
预定义字符集
预定义字符集是一组特殊的字符类,用于表示数字集、字母或任何非空格的集合。
\d
:匹配一个数字字符。等价于[0-9]
。\D
:匹配一个非数字字符。等价于[^0-9]
。\s
:匹配任何空白符,包括空格、制表符、换页符等。等价于[\f\n\r\t\v]
。\S
:匹配任何非空白符。等价于[^\f\n\r\t\v]
。\w
:匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]
。
\W
:匹配任何非单词字符。等价于[^A-Za-z0-9_]
。
1.3 重复匹配
重复匹配用来指定正则中一个字符、字符类,或者表达式可能重复的次数。
限定符
{m,n}
,其中m和n是十进制整数。这个限定符表示必须至少重复m次,最多重复n次。如a/{1,3}b将匹配’a/b’、‘a//b’、‘a///b’,但不匹配没有斜线的’ab’,或者’ab’。
*
:匹配0次或多次,等价于{0,}。+
:匹配1次或多次,等价于{1,}。?
:匹配0次或1次,等价于{0,1}。{n}
:n为非负整数,匹配n次。{m,n}
:m和n均为非负整数,其中m<=n。表示最少匹配m次,且最多匹配n次。如果省略m,则表示最少匹配0次;如果省略n,则表示最多匹配无限次,
贪婪匹配
限定符:*、+、?、{m,n} 具有贪婪性。当重复匹配时,尽可能多地重复它。
import re # 导入正则表达式模块 subject = 'gooooooogle' pattern = 'o{1,4}' # 正则表达式 matches = re.search(pattern,subject) # 执行匹配操作 matches = matches.group() # 读取匹配的字符串 print(matches) # 输出为 oooo
1.4 捕获组
定义组
组由’(‘和’)'元字符标记,将包含在其中的表达式组合在一起,可以使用重复限定符重复组的内容,如(ab)*将匹配ab零次或多次。group()、start()、end()或span() 方法可以获取匹配的信息。
import re p = re.compile('(ab)*') # 编译正则表达式 print(p.match('ababababab').span()) # 匹配范围 (0,10) p = re.compile('(a(b)c)d') # 编译正则表达式 m = p.match('abcd') print(m.group()) # 匹配'abcd' print(m.group(0)) # 匹配'abcd' print(m.group(1)) # 匹配'abc' print(m.group(2)) # 匹配'b' print(m.group(0,1,2)) # 匹配 ('abcd','abc','b') print(m.groups()) # 匹配 ('abc','b')
反向引用
引擎能够临时缓存在所有组装达式匹配的信息,并按照在正则表达式中从左至右的顺序进行编号,从1开始。每个缓冲区都可以使用’\n’访问,其中n为一个标识特定缓冲区的编号。
import re p = re.compile(r'\b(\w+)\s+\1\b') # 编译正则表达式,其中\1引用(\w+)匹配的内容 print(p.search('Paris in the the spring').group()) # 匹配:'the the'
1.5 命名组和非捕获组
命名组
除了默认的编号外,也可以为组定义一个别名,name是组的别名,命名组的行为与捕获组完全相同,并且将名称与组进行关联。
# 注意P字母大写 (?P<name>...) (?P=name) import re p = re.compile(r'(?P<word>\b\w+\b)') # 编译正则表达式 m = p.search('((((Lots of punctuation))))') # 匹配对象 print(m.group('word')) # 使用别名检索匹配结果 'Lots' print(m.group(1)) # 使用编号检索匹配结果 'Lots'
非捕获组
如果分组的目的仅仅是重复匹配表达式的内容,那么完全可以让引擎不缓存表达式匹配的信息,这样能够节省系统资源,提升执行效率。
(?:...) import re m = re.match('([abc])+','abc') # 匹配对象,缓存组的信息 print(m.groups()) # 结果 ('c',) m = re.match('(?:[abc])+','abc') # 匹配对象,没有缓存组的信息 print(m.groups()) # 结果 ()
1.6 边界断言
正则表达式大多数结构匹配的文本出现在最终的匹配结果中,但是也有些结构并不真正匹配文本,仅匹配一个位置而已,或者判斯某个位置左或右侧是否符合要求,这种结构被称为断言 (asserion) ,即零宽度匹配。
行定界符
^
:匹配行的开头。如果没有设置MULTILINE标志,只会在字符串的开头匹配。在MULTILINE模式下,^将在字符串中的每个换行符后立即匹配。
$
:匹配行的末尾,定义为字符串的结尾,或者后跟换行符的任何位置。
如果要匹配’^‘和’$'自身,可以使用转义字符:^。
import re print(re.search('^From','From Here to Eternity')) # <re.Match object; span=(0, 4), match='From'> print(re.search('^From','Reciting From Memory')) # None import re print(re.search('}$','{block}')) # <re.Match object; span=(6, 7), match='}'> print(re.search('}$','{block} ')) # None print(re.search('}$','{block}\n')) # <re.Match object; span=(6, 7), match='}'>
头尾定界符
\A
:仅匹配字符串的开头,当不在MULTILINE模式下时,\A和匹配位置相同。在MULTILINE模式下,\A仍然匹配字符串的开头,但可以匹配在换行符之后的字符串内的任何位置。\Z
:只匹配字符串末尾。
单词定界符
\b
:匹配单词的边界,即仅在单词的开头或结尾位置匹配。单词的边界由空格或非字母的数字字符表示。\B
:与\b相反,仅在当前位置不在单词边界时才匹配。
import re p = re.compile(r'\bclass\b') print(p.search('no class at all')) # <re. Match object; span=(3, 8), match='class"> print(p.search('the declassified algorithm')) # None print(p.search('one subclass is')) # None import re p = re.compile('\bclass\b') # 编译正则表达式 print(p.search('no class at all')) # None print(p.search('\b'+'class'+'\b')) # <re.Match object; span=(0, 7), match='\x08class\x08'>
字符串中,\b表示退格字符。
1.7 环视
环视也是一种零宽断言,是指在某个位置向左或向右,保证其左或右侧必须出现某类字符,包括单词字符\w和非单词字符\W。环视也只是一个判断,匹配一个位置,本身不匹配任何字符。
- 正前瞻:使用
(?=…)
,定义表达式后面必须满足特定的匹配条件。如a(?=\d),仅匹配后面包含数字字母a。 - 负前瞻:使用
(?!…)
,定义表达式后面必须不满足特定的匹配条件。如a(?!\d),仅匹配后面不包含数字字母a。 - 正回顾:使用`(?<=…),定义表达式前面必须满足特定的匹配条件。如(?<=\d)a,仅匹配前面包含数字字母a。
- 负回顾:使用
(?<!…)
,定义表达式前面必须不满足特定的匹配条件。如(?<!\d)a,仅匹配前面包含数字字母a。
1.8 选择和条件
选择匹配
|
或者or
运算符表示选择匹配。如果A|B是正则表达式,那么A|B将匹配任何与A或B匹配的字符串。|具有非常低的优先级,因此A和B将尽可能包含整个字符串,如Crow|Servo将匹配’Crow’或’Servo’,而不是’Cro’、‘w’或’S’、‘ervo’。
条件匹配
(?(id/name)yes-pattern|no-pattern) ''' id表示分组编号,name表示分组的别名,如果对应的分组匹配到字符, 则选择yes-pattern子表达式执行匹配; 如果对应的分组没有匹配字符,则选择no-pattern子表达式执行匹配。 ''' (?(id/name)yes-pattern) import re pattern = '((<)?/?\w+(?(2)>))' s = '<b>html</b><span>html</span>' print(re.findall(pattern,s)) # [('<b>', '<'), ('html', ''), ('</b>', '<'), ('<span>', '<'), ('html', ''), ('</span>', '<')]
1.9 编译标志
编译标志允许修正正则表达式的工作方式,控制匹配模式。标志在re模块中有两个名称,一个是长名称,如IGNORGECASE;一个是简短的单字母,如I。多个标识可通过按位或运算同时设置,如re.I|re.M。
re.I
:长名re.IGNORECASE,执行不区分大小写的匹配,字符类和字面字符串将忽略大小写匹配字母。re.L
:长名re.LOCALE,语言依赖,使\w、\W、\b、\B和大小写敏感匹配依赖于当前区域而不是Unicode数据库。re.M
:长名re.MULTILINE,多行模式,通常只匹配字符串的开头,$只匹配字符串的结尾,紧跟在字符串末尾的换行符(如果有)之前。当指定了这个标志时,匹配字符串的开头和字符串中每一行的开头,紧跟在每个换行符之后:$匹配字符串的结尾和每行的结尾,紧跟在每个换行符之前。re.S
:长名re.DOTALL,点匹配全部字符,使’.‘元字符匹配任何字符,包括换行符。没有这个标志,’.'将匹配除类换行符的任何字符。re.U
:长名re.UNICODE,Unicode匹配,根据 Unicode 字符集解析字符。预定义字符类\w、\W、\b、\B、\s、\S、\d、\D取决于Unicode定义的字符属性。re.A
:长名re.ASCII,使\w、\W、\b、\B、\s和\S执行仅匹配ASCII,而不是完整匹配Unicode,这仅对Unicode模式有意义,并且对于字节模式将被忽略。
re.X
:长名re.VERBOSE,冗长匹配,此标志允许编写更易读的正则表达式,提供更灵活的格式方法。
import re subject = 'My username is Css888!' pattern = r'css\d{3}' # 正则表达式 matches = re.search(pattern,subject,re.I|re.M) # 执行匹配的操作 matches = matches.group() # 读取匹配的字符串 print(matches) # Css888
1.10 注释
正则表达式中添加注释信息,便于阅读和维护。
(?#注释信息) a(?#匹配字符 abc)bc charref = re.compile(r""" &[#] # 前缀标识符 ( 0[0-7]+ [0-9]+ x[0-9a-fA-F]+ ); """,re.VERBOSE) charref = re.compile("�[0-7]+[0-9]+x[0-9a-fA-F]+")
2. 使用 re 模块
2.1 初用re模块
操作步骤
- 导入re模块。
- 定义正则表达式。
- 使用re.compile()函数将正则表达式字符串编译为正则表达式对象(pattern实例)。
- 使用Pattern实例处理文本,并获取匹配结果(Match实例)。
- 使用Match实例获取匹配信息。
import re # 导入正则表达式模块 pattern = 'hello' # 定义正则表达式字符串 pattern = re.compile(pattern) # 将正则表达式字符串编译称Pattern对象 match = pattern.match('hello world!') # 使用Pattern实例的方法处理文本 if match: # 获取匹配结果,无法匹配时将返回None print(match.group()) # 使用group()方法获取匹配结果的分组信息 else: print('None') # 输出 hello
2.2 认识re模块
re模块提供了两套一一对应的接口,名称相同、功能相同、用法相近。一套时附加在re类对象上的模块函数;另一套时附加在Pattern对象上的实例方法,语法对比如下:
re.match(pattern,string,flag=0) # 模块函数 Pattern.mattern(string[,pos[,endpos]]) # 实例方法 # 参数pattern表示字符串,flag表示标志变量,默认为0
re.compile(pattern,flags=0)
:将正则表达式字符串编译为一个正则表达式对象。re.search(pattern,string,flags=0)
:扫描整个字符串找到匹配样式的第一个位置。re.match(pattern,flags=0)
:如果string开始匹配正则表达式,返回相应的匹配对象,如果没有匹配,则返回None。re.fullmatch(pattern,string,flags=0)
:如果string匹配到正则表达式,返回一个相应的匹配对象,否则返回None。re.split(pattern,string,maxsplit=0,flags=0)
:使用pattern分开string。参数maxsplit设置最多分开次数,剩下的字符返回到列表的最后一个元素。re.findall(pattern,string,flags=0)
:对string返回一个不重复的pattern匹配列表,string从左到右进行扫描,匹配按找到的顺序返回。re.finditer(pattern,string,flags=0)
:pattern在string里所有的非重复匹配,返回为一个迭代器iterator保存了匹配对象。string从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。re.sub(pattern,repl,string,count=0,flags=0)
:使用repl替换在string最左边非重复出现的pattern而获得的字符串,如果没有找到,则返回原string。re.subn(pattern,repl,string,count=0,flags=0)
:行为与sub()相同,但是返回一个元组。re.purge()
:清除正则表达式的缓存。
re.error(msg,pattern=None,pos=None)
:抛出一个异常。
import re regex = '#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}' # 定义正则表达式字符串 pattern = re.compile(regex,re.I) # 生成正则表达式对象 string = "#ffbbad#Fc01DF #FFF #ffE" # 定义字符串 print(pattern.findall(string)) # 输出 ['#ffbbad', '#Fc01DF', '#FFF', '#ffE']
2.3 正则表达式对象
Pattern 对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行批评操作。Pattern 不能直接实例化对象,可以使用re.compile()函数构造。
search(string[,pos[,endpos]])
:扫描整个string,寻找一个匹配的位置,并返回一个匹配对象。如果没有匹配,则返回None。match(string[,pos[,endpos]])
:如果string开始匹配正则表达式,返回相应的匹配对象,如果没有匹配,则返回None。如果想定位匹配在string中的位置,则使用search()替代。fullmatch(string[,pos[,endpos]])
:如果整个string匹配正则表达式,返回一个相应的匹配对象,否则返回None。findall(string[,pos[,endpos]])
:搜索string,以列表形式返回全部能匹配的子串。finditer(string[,pos[,endpos]])
:搜索string,返回一个顺序访问每个匹配结果(Match 对象)的迭代器。sub(repl[,pos[,endpos]])
:使用参数repl替换string中每一个匹配的子串,然后返回替换后的字符串。sub(repl[,pos[,endpos]])
:使用参数repl替换string中每一个匹配的子串,然后返回替换后的字符串。
split(string[,maxsplit])
:按照能够匹配的子串将string分割,返回列表。maxsplit用于指定最大分割次数,不指定时默认全部分割。
pattern = re.compile("d") print( pattern.search("dog") ) # <re.Match object; span=(0, 1), match='d'> print( pattern.search("dog", 1) ) # None pattern = re.compile("o") print( pattern.match("dog") ) # None print( pattern.match("dog", 1)) # <re.Match object; span=(1, 2), match='o'> import re subject = 'Cats are smarter than dogs' pattern = re.compile(r'\W+') # 将正则表达式编译为Pattern对象 matches = pattern.subn("_", subject) # 执行替换操作 print(matches) # ('Cats_are_smarter_than_dogs', 4)
2.4 匹配对象
Match 对象表示一个匹配的结果,包含了本次匹配的相关信息。匹配对象总是一个布尔值,如果匹配到结果,则返回True;否则返回None。
match = re.search(pattern,string) if match: # 检测是否匹配到结果 process(match) # 处理匹配结果
方法
group([group1,…])
:获取一个或多个分组匹配的字符串。groups()
:以元组形式返回全部分组匹配的字符串。groupdict()
:以字典的形式返回定义别名的分组信息,字典元素以别名为键、以该组匹配的子串为值。
start([group]):返回指定的组截获的子串在string中的起始索引,group默认为0.
end([group]):返回指定的组截获的子串在string中的结束索引。
span([group]):返回(start(group),end(group))。
expand(template):将匹配到的分组代入template中,然后返回。
import re # 导入正则表达式模块 m= re.match(r'(\w+)(\w+)(?P<sign>.*)', 'hello world!') print("m.string:", m.string) # m.string: hello world! print("m.re:", m.re) # m.re: re.compile('(\\w+)(\\w+)(?P<sign>.*)') print("m.pos:", m.pos) # m.pos: 0 print("m.endpos:", m.endpos) # m.endpos: 12 print("m.lastindex:", m.lastindex) # m.lastindex: 3 print("m.lastgroup:", m.lastgroup) # m.lastgroup: sign print("m.group(1,2):", m.group(1, 2)) # m.group(1,2): ('hell', 'o') print("m groups():", m.groups()) # m groups(): ('hell', 'o', ' world!') print("m groupdict().", m.groupdict()) # m groupdict(). {'sign': ' world!'} print("m.start(2):", m.start(2)) # m.start(2): 4 print("m.end(2):", m.end(2)) # m.end(2): 5 print("m.span(2):", m.span(2)) # m.span(2): (4, 5) print(r"m.expand(r'\2\1\3'):", m.expand(r'\2\1\3')) # m.expand(r'\2\1\3'): ohell world!