11.Python 正则表达式

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字符集中的任意一个字母。

在字符类中,元字符不再表示特殊的含义。如[abc ] 将 匹 配 ′ a ′ 、 ′ b ′ 、 ′ c ′ 或 ′ ]将匹配'a'、'b'、'c'或' ]abc’中任意一个字符,’$'本身是一个元字符,但在字符类中仅能匹配字符自身。

在字符类中,如果要匹配这4个元字符:’[’、’]’、’-’、’’,需要在字符左侧添加反斜杠进行转义,或者把’[’、’]’、’-‘作为字符类中第一个字符,把’'作为字符类中非第一个字符。

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

惰性匹配

惰性匹配又称非贪婪性匹配。在限定符后面加上?可以实现非贪婪或者最小匹配。

*? +? ?? {m,n}?

import re # 导入正则表达式模块
s = '<html><head><title>Title</title>' # 定义字符串
print(re.match('<.*>', s).span()) # 匹配范围:(0,32)
print(re.match('<.*>', s).group()) # 匹配结果:<html><head><title>Title<title>
print(re.match('<.*?>', s).group()) # 匹配结果:<html>

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[0-7]+[0-9]+x[0-9a-fA-F]+")

2. 使用 re 模块

2.1 初用re模块

操作步骤

  1. 导入re模块。
  2. 定义正则表达式。
  3. 使用re.compile()函数将正则表达式字符串编译为正则表达式对象(pattern实例)。
  4. 使用Pattern实例处理文本,并获取匹配结果(Match实例)。
  5. 使用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中每一个匹配的子串,然后返回替换后的字符串。
  • subn(repl[,pos[,endpos]]):与re.sub()函数的功能相同,但是返回替换字符串,以及替换的次数。
  • 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!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有请小发菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值