当前位置: 首页 > news >正文

3 x 10的python表达式_part10-3 Python常见模块(正则表达式)

六、 Python 正则表达式

正则表达式(Regular Expression)用于描述一种字符串匹配的模式(Pattern),即可用于检查一个字符串是否含有某个子串,也可用于从字符串中提取匹配到的子串,或者对字符串中匹配到的子串执行替换操作。

正则表达式是一个非常实用的工具,它包含的知识点较多,它的模式匹配能力也非常强,学习需要由浅入深的学习。

熟练使用正则表达式是一个很重要的技能。可用正则表达式来开发数据抓取、网络爬虫等程序。在 Python 中的正则表达式就是几个常用函数,难点是正则表达式字符串的开发。

1、 Python 的正则表达式支持

导入 re 模块后,可使用 re.__all__ 命令查看该模块所包含的全部属性和函数。示例如下:

>>> import re

>>> re.__all__

['match', 'fullmatch', 'search', 'sub', 'subn', 'split', 'findall', 'finditer', 'compile', 'purge', 'template', 'escape', 'error', 'A', 'I', 'L', 'M', 'S', 'X', 'U', 'ASCII', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', 'VERBOSE', 'UNICODE']

re 模块中的常用函数介绍:

(1)、re.compile(pattern,flags=0):该函数用于将 pattern 代表的正则表达式字符串编译成 _sre.SRE_Pattern 对象,该对象是正则表达式编译之后在内存中的对象,编译后的对象可以缓存并复用正则表达式字符串。在代码中如果需要多次使用同一个正则表达式字符串,则可以先编译后再使用,这样可提高运行效率。

参数 flags 表示正则表达式的旗标。经过编译后的 _sre.SRE_Pattern 对象可以调用 re 模块中大部分函数。例如下面代码所示,将编译后的 _sre.SRE_Pattern 对象调用 re 模块的 search() 方法进行匹配。

import re

# 先编译正则表达式

p = re.compile('abc')

# 调用 _sre.SRE_Pattern 对象的 search() 方法 进行匹配

print(p.search('www.abc.com'))

# 下面代码直接使用 re 模块的 search() 函数匹配目标字符串

print(re.search('abc', 'www.abc.com'))

运行结果如下所示:

从输出可以看到,两次调用 search() 函数匹配到的结果是一样的,但是第一种方式预编译了正则表达式,可以复用 p 对象(该对象缓存了正则表达式字符串),有更好的性能。

(2)、re.match(pattern,string,flags=0):根据 pattern 的正则模式从 string 字符串的开始位置进行匹配。如果从开始位置匹配不成功,match() 函数就返回 None。flags 参数代表正则表达式的匹配旗标。该函数返回 _sre.SRE_Match 对象,该对象包含的 span(n) 方法用于获取第 n+1 个组的匹配位置,group(n) 方法用于获取第 n+1 个组所匹配的子串。

(3)、re.search(pattern, string, flags=0):根据 pattern 的正则模式在 string 代表的字符串中进行扫描,并返回字符串中第一处匹配 pattern 的匹配对象。flags 参数同样代表正则表达式的匹配旗标。该函数也返回 _sre.SRE_Match 对象。

match() 与 search() 的区别在于:match() 必须从字符串开始处就匹配,但 search() 可以搜索整个字符串。示例如下:

import re

m1 = re.match('www', 'www.michael.com') # 从字符串开始处匹配

print(m1.span()) # span 返回匹配的位置,(0, 3)

print(m1.group()) # group 返回匹配的组,www

print(re.match('mich', 'www.mich.com')) # 如果从开始位置匹配不到,返回 None

m2 = re.search('www', 'www.michael.com') # 从开始位置匹配

print(m2.span()) # (0, 3)

print(m2.group()) # www

m3 = re.search('mich', 'www.michael.com') # 从中间位置开始匹配,返回 Match 对象

print(m3.span()) # (4, 8)

print(m3.group()) # mich

运行代码,输出结果如下:

(0, 3)

www

None

(0, 3)

www

(4, 8)

mich

从上面代码的输出可知,match() 函数要求必须从字符串开始处匹配,而 search() 函数是扫描整个字符串,可以从字符串的任意位置开始匹配。

(4)、re.findall(pattern, string, flags=0):根据 pattern 的匹配模式对 string 字符串整个扫描,并返回字符串中所有与pattern 模式匹配的子串组成的列表。flags 参数同样是正则表达式的匹配旗标。

(5)、re.finditer(pattern, string, flags=0):根据 pattern 的匹配模式对 string 字符串整个扫描,并返回字符串中所有与 pattern 模式匹配的子串组成的迭代器,迭代器的元素是 _sre.SRE_Match 对象。flags 参数同样是正则表达式的匹配旗标。

findall() 和 finditer() 函数的功能基本相同,不同的是在于它们的返回值,findall() 返回所有匹配 pattern 的子串组成的

列表;而 finditer() 返回所有匹配 pattern 的子串组成的迭代器。

另外,search() 与 findall()、finditer() 的区别是,search() 只返回字符串中第一个匹配 pattern 的子串;而 findall() 和 finditer() 则返回字符串所有匹配 pattern 的子串。

findall() 和 finditer() 的使用示例如下:

import re

# 返回所有匹配 pattern 的子串组成的列表,re.I 旗标表示忽略大小写

print(re.findall('py', 'Py is very good, Py.org is official website', re.I))

# 返回所有匹配 pattern 的子串组成的迭代器,忽略大小写

it = re.finditer('py', 'Py is very good, Py.org is official website', re.I)

for i in it:

print(str(i.span()) + "-->" + i.group())

运行代码,输出结果如下:

['Py', 'Py']

(0, 2)-->Py

(17, 19)-->Py

(6)、 re.fullmatch(pattern, string, flags=0):该函数要求整个string字符串能匹配 pattern,如果匹配则返回包含匹配信息的 _sre.SRE_Match 对象;否则返回 None。

(7)、re.sub(pattern, repl, string, count=0, flags=0):该函数用于将 string 字符串所有匹配 pattern 的内容替换成 repl;repl 可以是字符串,也可以是一个函数。count 参数控制最多替换多少次,如果指定 count为0,则表示全部替换。

sub() 函数的用法示例如下:

import re

my_date = '2019-11-11'

# 将 my_date 字符串中的 - 替换为 /

print(re.sub(r'-', '/', my_date))

# 只做一次替换

print(re.sub(r'-', '/', my_date, 1))

输出如下所示:

2019/11/11

2019/11-11

上面代码中的 r'-' 是原始字符串,r 代表原始字符串,可以避免对字符串中的特殊字符进行转义。sub() 在执行替换时可以基于被替换的内容进行改变。例如下面代码将字符串中的每个英文单词都变成一本图书的名字。示例如下:

import re

# 在匹配的字符串前后添加内容

def func(matched):

# matched 就是匹配对象,通过该对象的 group() 方法可以获取被匹配的字符串

result = '《' + matched.group('lang') + "入门到高级》"

return result

s = 'Python 很好, Linux 也很好'

# 对 s 里面的英文单词进行替换,用 re.A 旗标控制单词

print(re.sub(r'(?P\w+)', func, s, flags=re.A))

运行结果如下所示:

《Python入门到高级》 很好, 《Linux入门到高级》 也很好

从输出结果可以看出,使用 sub() 函数执行替换时,指定使用 func() 函数作为替换内容,而 func() 函数负责在 pattern 匹配的字符串之前添加 “《”,在 pattern 匹配的字符串之后添加 “入门到高级》”。所以会看到上面的输出结果。

r'(?P\w+)' 正则表达式用圆括号表达式创建了一个组,并使用 “?P” 选项为该组起名为 lang,所起的组名要放在尖括号内。剩下的 “\w+” 才是正则表达式的内容,其中 “\w” 代表任意字符;“+” 限定前面的 “\w” 可出现一次到多次,因此,“\w+” 代表一个或多个任意字符。由于在后面指定的 re.A 选项,这样 “\w” 就只能匹配 ASCII 字符,不能匹配汉字。

在使用 sub() 函数执行替换时正则表达式 “\w+” 所匹配的内容可以通过组名 “lang” 来获取,这样 func() 函数就调用了 matched.group('lang') 来获取 “\w+” 所匹配的内容。

(8)、re.split(pattern, string, maxsplit=0, flags=0):使用 pattern 对 string 进行分割,该函数返回分割得到的多个子串组成的列表。maxsplit 参数控制最多分割多少次,默认全部分割。

split() 函数用法示例如下:

import re

# 使用逗号对字符串进行分割

print(re.split(', ', 'python, linux, java'))

# 输出:['python', 'linux', 'java']

# 指定只分割一次,被分割成两个子串

print(re.split(', ', 'python, linux, java', 1))

# 输出:['python', 'linux, java']

# 使用 n 进行分割,未匹配成功时就不分割,比如使用 b 字符进行分割

print(re.split('n', 'python, linux, java'))

# 输出:['pytho', ', li', 'ux, java']

(9)、re.purge():清除正则表达式缓存。

(10)、re.excape(pattern):对模式中除 ASCII 字符、数值、下划线(_)之外的其他字符进行转义。也就是对模式中的特殊字符进行转义。

escape() 函数的用法示例如下:

import re

# 对模式中的特殊字符进行转义

print(re.escape(r'www.michael.com is very strong, I like it! 666'))

# 输出:www\.michael\.com\ is\ very\ strong\,\ I\ like\ it\!\ 666

print(re.escape(r'A-Zand0-9?'))

# 输出:A\-Zand0\-9\?

从上面代码的输出结果可知,escape() 函数对非 ASCII 字符、数值、下划线(_)之外的其他字符都进行了转义。

在 re 模块中还包含有两个类,分别是正则表达式对象(类型是 _sre.SRE_Pattern)和匹配(Match)对象。其中正则表达式对象是调用 re.compile() 函数的返回值,该对象的方法与 re 模块中的函数大致对应。但是正则表达对象的 search()、match()、fullmatch()、findall()、finditer()方法的功能要强大一些,因为这些方法可以额外指定 pos 和 endpos 两个参数,用于指定只处理目标字符串从 pos 开始到 endpos 结束之间的子串。下面代码示例用正则表达式对象的方法来执行匹配。代码如下:

import re

# 首先编译得到正则表达式对象,从类的角度来看就是在创建对象(或实例)pa

pa = re.compile('mich')

# 调用 pa 对象的 match 方法,该方法原本是从开始位置匹配的,

# 但是在这里可以指定开始匹配的位置,指定从索引 4 开始匹配

print(pa.match('www.michael.com', 4).span()) # (4, 8)

# 下面指定索引起始和结束位置,指定索引4 到 6 之间执行匹配,匹配失败

print(pa.match('www.michael.com', 4, 15)) # None

# 下面指定从索引 4 到索引 8 之间执行完全匹配,匹配成功

print(pa.fullmatch('www.michael.com', 4, 8).span()) # (4, 8)

上面代码中使用正则表达式对象来调用 match()、fullmatch()方法时指定了 pos 和 endpos 参数,这样可以只处理目标字符串的中间一段。在使用 compile() 函数编译正则表达式后,该函数所返回的对象就会缓存该正则表达式,从而可以多次调用该正则表达式执行匹配。例如上面代码多次使用了 pa 对象来执行匹配。

re 模块中的 Match 对象(类型是 _sre.SRE_Match)是 match()、search() 方法的返回值,该对象中包含了详细的正则表达式匹配信息,包括正则表达匹配的位置、正则表达式匹配的子串。

_sre.SRE_Match 对象(匹配对象)包含的方法和属性有下面这些:

(1)、match.group([group1, ...]):获取该匹配对象中指定组所匹配的字符串。

(2)、match.__getitem__(g):这是 match.group(g) 的简化写法。由于 match 对象提供了 __getitem__() 方法,因此程序可使用 match[g] 来代替 match.group(g)。

(3)、match.groups(default=None):返回 match 对象中所有组所匹配的字符串组成的元组。

(4)、match.groupdict(default=None):返回 match 对象中所有组所匹配的字符串组成的字典。

(5)、match.start([group]):获取该匹配对象中指定组所匹配的字符串的开始位置。

(6)、match.end([group]):获取该匹配对象中指定组所匹配的字符串的结束位置。

(7)、match.span([group]):获取该匹配对象中指定组所匹配的字符串的开始位置和结束位置。该方法相当于同时返回 start() 和 end() 方法的返回值。

上面7个方法都涉及到组,在正则表达式中,用圆括号将多个表达式括起来就形成组。如果正则表达式中没有圆括号,则整个表达式就属于一个默认组。下面代码示例了使用组的情形:

import re

# 在正则表达式中使用组

m = re.search(r'(?Ppython).(?Porg)', r'www.python.org is official website')

print(m.group(0)) # python.org

# 调用简化的写法,底层是调用 m.__getitem__(0)

print(m[0]) # python.org

print(m.span(0)) # (4, 14)

print(m.group(1)) # python

# 调用简化的写法,底层是调用 m.__getitem__(1)

print(m[1]) # python

print(m.span(1)) # (4, 10)

print(m.group(2)) # org

# 调用简化的写法,底层是调用 m.__getitem__(2)

print(m[2]) # org

print(m.span(2)) # (11, 14)

# 返回所有组所匹配的字符串组成的元组

print(m.groups()) # ('python', 'org')

# 要返回字典的情况时,必须要为组起一个名字,名字就是字典的键

print(m.groupdict()) # {'py': 'python', 'o': 'org'}

运行代码,输出结果如下:

python.org

python.org

(4, 14)

python

python

(4, 10)

org

org

(11, 14)

('python', 'org')

{'py': 'python', 'o': 'org'}

上面代码中 search() 函数的正则表达式是 r'(?Ppython).(?Porg)',这个正则表达式中有两个组,并且每个组都起了一个名字。接下来的代码中可以依次获取 group(0)、group(1)、group(2)的值,依次获取的是整个正则表达式所匹配的子串、第一个组匹配的子串、第二个组匹配的子串;也可以依次获取 span(0)、span(1)、span(2)的值,依次获取整个正则表达式所匹配子串的开始和结束位置、第一个组匹配子串的开始和结束位置、第二个组匹配子串的开始和结束位置。

只要正则表达式能匹配到结果,则不管正则表达是否包含组,group(0)、span(0)都能获取到内容。其中 group(0) 是获取整个匹配到的整个子串,span(0) 获取到的是整个子串的开始和结束位置。

在正则表达式中,使用 ?P 方式为组指定名字后,就可以调用 _sre.SRE_Match 对象(匹配对象)的 groupdict() 方法获得字典,字典的 key 是为组指定的名字,value 是匹配到的单组结果。

下面继续说下 _sre.SRE_Match 对象(匹配对象)的方法:

(8)、match.pos:该属性返回传给正则表达式对象的 search()、match() 等方法的 pos 参数。

(9)、match.endpos:是传给正则表达式对象的 search()、match()等方法的 endpos 参数值。

(10)、match.lastindex:最后一个正则匹配的捕获组的整数索引。如果没有组匹配,该属性值是 None。例如用 (a)b、((a)(b))或 ((ab)) 对字符串 'ab' 执行匹配,该属性都会返回 1;但如果使用 (a)(b) 正则表达式对 'ab' 执行匹配,则 lastindex 等于 2。

(11)、match.lastgroup:返回最一个匹配的捕获组的名字;如果该组没有名字或根本没有组匹配,该属性返回 None。

(12)、match.re:返回执行正则表达式匹配时所用的正则表达式。

(13)、match.string:返回执行正则表达式匹配时所用的字符串。被匹配的字符串string。

2、 正则表达式旗标

Python 的正则表达式旗标都使用 re 模块中的属性来代表,旗标有下面这些:

(1)、re.A 或 re.ASCII:控制\w,\W,\b,\B,\d,\D,\s,\S 这些特殊符号只匹配 ASCII 字符,而不是匹配所有的 Unicode 字符。也可在正则表达式中使用 (?a) 行为旗标来代表。

(2)、re.DEBUG:显示编译正则表达式的 Debug 信息,没有行为旗标。

(3)、re.I 或 re.IGNORECASE:在匹配时不区分大小写。对应的行为旗标是 (?i)。

re.I 旗标示例如下:

#默认要区分大小写,所以无匹配结果

>>> importre>>> re.findall(r'pyth', 'Python is good, PYTHON is good')

[]#使用 re.I 旗标后,限定不区分大小写,现在有匹配结果

>>> re.findall(r'pyth', 'Python is good, PYTHON is good', re.I)

['Pyth', 'PYTH']#使用行为旗标 (?i) 限定不区分大小写,同样能匹配到结果

>>> re.findall(r'(?i)pyth', 'Python is good, PYTHON is good')

['Pyth', 'PYTH']

(4)、re.L 或 re.LOCALE:根据当前区域设置使用正则表达式匹配时不区分大小写。该旗标只对 bytes 模式起作用,对应的行内旗标是 (?L)。

(5)、re.M 或 re.MULTILINE:匹配多行模式的旗标。使用该旗标后,可以匹配每一行的开头,以及 “^” 符号能匹配字符串的开头;同时 “$” 符号能匹配字符串的末尾和每一行的末尾。没有该旗标的正则表达式,“^” 符号只匹配字符串的开头,“$” 符号只匹配字符串的结尾,或者匹配到字符串默认的换行符(如果有)之前。对应的行为旗标是 (?m)。

(6)、re.S 或 re.DOTALL:指定该旗标后,点(.)符号可以匹配换行符,默认不匹配换行符。行内旗标是 (?s)。

(7)、re.U 或 re.Unicode:控制\w,\W,\b,\B,\d,\D,\s,\S 这些特殊符号能匹配所有的 Unicode 字符。在 Python 3.x 版本中,默认匹配的就是所有 Unicode 字符,所以该旗标基本上没什么用。

(8)、re.X 或 re.VERBOSE:使用该旗标后,正则表达式可以换行,还可以为正则表达式添加注释,提高正则表达式可读性。对应的行内旗标是 (?x)。

re.X 旗标的用法示例如下:

importre

a= re.compile(r"""\d{3} # 地方区号

\- # 号码分界线

\d{8} # 匹配8个数字""", re.X)

b= re.compile(r'\d{3}\-\d{8}')print(a.search('你好,我成都的电话号码是:028-12345678').group())print(b.search('你好,我首都的电话号码是:010-87654321').group())

运行代码,输出结果如下所示:

028-12345678

010-87654321

上面代码中在编译第一个正则表达式时使用 re.X 旗标,因此在表达式中可以换行,还可添加注释。将编译后的对象赋值给 a 变量,通过后面的调用语句的输出结果可以看到,该正则表达式完全没有问题。

3、 创建正则表达式

正则表达式用于匹配一批字符串,正则表达式同时也是一个特殊的字符串。在正则表达中有它可以支持的合法字符、特殊字符、预定义字符、方括号表达式、边界匹配符。这些都是有特殊用途的。

(1)、正则表达式所支持的合法字符如下表所示

字符释义

x

x表示任意合法的字符

\uhhhh

十六进制值0xhhhh所表示的Unicode字符

\t

制表符('\u0009')

\n

新行(换行)符('\u000A')

\r

回车符('\u000D')

\f

换页符('\u000C')

\a

报警(bell)符('\u0007')

\e

Escape符('\u001B')

\cx

x对应控制符,例如,\cM匹配Ctrl+M。x值必须是A~Z或a~z之一

(2)、正则表达式中的特殊字符

特殊字符有特殊用途,比如反斜线(\)是转义符。要匹配这些特殊字符,需要先将这些特殊字符转义,也就是在这些特殊字符前面添加一个反斜线(\)。正则表达式中的特殊字符如下所示:

特殊字符说明

$

匹配一行的结尾。要匹配$字符本身,就使用$(下同)

^

匹配一行的开头

()

标记子表达式(也就是组)的开始位置和结束位置

[]

用于确定中括号表达式的开始位置和结束位置

{}

用于标记前面子表达式的出现次数,如{m,n}匹配前一个字符m到n次

*

指定前面的子表达式可以出现0次或多次

+

指定前面的子表达至少要出现1次,也可以出现多次

?

指定前面的子表达式可以出现0次或1次

.

匹配除换行符之外的任意单个字符

\

用于转义特殊字符,或者用于指定八进制、十六进制字符

|

指定在两项之间任选一项

在正则表达中,通常都需要将多个字符拼接起来。示例如下:

>>> print(re.fullmatch(r'\u0041\\', 'A\\')) #匹配到:A\

>>> print(re.fullmatch(r'\u0061\t', 'a\t')) #匹配到:a

>>> print(re.search(r'\?\[', 'python?[isgood')) #匹配到:?[

有了上面的这些特殊字符,在正则表达式中还需要使用通配符,通配符是可以匹配多个字符的特殊字符。正则表达式中的通配符被称为“预定义字符”。

(3)、 正则表达式所支持的预定义字符

预定义字符有下面7个:

预定义字符说明

.

默认可以匹配除换行符之外的任意字符。可使用re.S旗标来增加匹配换行符

\d

匹配0~9的所有数字

\D

匹配非数字

\s

匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等

\S

匹配所有的非空白字符

\w

匹配所有的单词字符,包括0~9的所有数字、26个英文字母和下划线(_)、汉字

\W

匹配所有非单词字符

>>> re.findall(r'c\wt', 'cat, cbt, c8t, c_t') #匹配结果如下所示

['cat', 'cbt', 'c8t', 'c_t']>>> re.findall(r'\d\d\d-\d\d\d-\d\d\d\d', '400-800-1234, 8001-5553-6666') #匹配结果如下所示

['400-800-1234']

有了上面这些预定义字符,还需要进一步使用方括号表达式来丰富正则表达式。

(4)、方括号表达式

方括号表达式说明

表示枚举

例如[abc],表示a、b、c其中任意一个字符

表示范围

例如[a-f],表示 a~f 范围内的任意字符;[\u0041-\u0056]表示十六进制字符\u0041到\u0056范围的字符。范围可以和枚举结合使用,如[a-cx-z]表示a~c、x~z范围内的任意字符

表示求否:^

例如abc表示非a、b、c的任意字符;a-f表示不是a~f范围的任意字符

方括号表达式使用灵活,几乎可以匹配任意字符。例如要匹配所有的中文字符,可以利用 [\\u0041-\\u0056] 的形式,因为所有的中文字符的 Unicode 值是连续的,只要找出所有中文字符中最小、最大的 Unicode 值,就可以利用这种形式来匹配出所有中文字符。

(5)、边界匹配符

Python 的正则表达式还有边界匹配符,可用于匹配行的开头、结尾,单词的边界等。边界匹配符如下所示:

边界匹配符说明

^

行的开头

$

行的结尾

\b

单词的边界,即只能匹配单词前后的空白

\B

非单词的边界,即只能匹配不在单词前后的空白

\A

只匹配字符串的开头

\Z

只匹配字符串的结尾,仅用于最后的结束符

4、 子表达式

正则表达式还可以使用圆括号表达式,可将多表达式组成一个子表达式,在圆括号中可以使用“或”运算符(|)。圆括号表达式也是功能丰富的用法之一。圆括号表达式,也叫子表达式(组)支持的用法如下:

(1)、(exp):匹配 exp 表达式并捕获成一个自动命名的组,后面可通过 “\1” 引用第一个捕获组所匹配的子串,通过 “\2” 引用第二个捕获组所匹配的子串,......,以此类推。示例如下:

>>> re.search(r'Windows (95|98|NT|2000)[\w ]+\1', 'Windows 98 publised in 98')<_sre.sre_match object span="(0," match="Windows 98 publised in 98">

上面代码用到的正则表达式是 r'Windows (95|98|NT|2000)[\w ]+\1';紧接着的 [\w ]+ 表达式可匹配任意单词字符和空格,方括号后面的 “+” 表示方括号可出现1次或多次;最后是 “\1” 表示引用第一个组所匹配到的子串,假如第一个匹配到的是98,则 “\1”也必须是98,因此该正则表达式可匹配 “Windows 98 publised in 98”。如果将上面代码改为如下形式就不能匹配:

>>> print(re.search(r'Windows (95|98|NT|2000)[\w ]+\1', 'Windows 98 publised in 95'))

None

从输出可看出,未能成功匹配到结果,是因为第一个组匹配到的子串是98,因此 “\1” 也必须引用子串98,所以该正则表达式不能匹配 “Windows 98 publised in 95”

(2)、(?Pexp):匹配 exp 表达式并捕获成命名组,该组的名字为 name。后面可通过 (?P=name) 来引用前面捕获的组。通过此处介绍可以看出,(exp) 和 (?Pexp) 的功能大致相同,只是 (exp) 捕获的组没有显式指定组名,因此后面只能使用\1、\2等方式来引用这种组所匹配的子串;而 (?Pexp) 捕获的组有名称,因此后面可通过 (?P=name) 的方式来引用命名组所匹配的子串。

(3)、(?P=name):引用 name 命名组所匹配的子串。

示例代码如下所示:

>>> re.search(r'\w+)>\w+(?P=tag)>', '

hello
')<_sre.sre_match object span="(0," match="<div>hello</div>">

上面代码中的正则表达式是 r'\w+)>\w+(?P=tag)>',表达式开始是 “\w+),组名是 tag,该组可以匹配1个或多个任意字符;紧接着后面是 “>” 符号,这部分用于匹配一个 HTML 或 XML 标签。尖括号外面的 “\w+” 用于匹配标签中的内容;后面定义的 “” 直接匹配这两个字符;之后的 (?P=tag) 就用于引用前面的 tag 组所匹配的子串,也就是说,该正则表达式要求内容必须在合理关闭的 HTML 或 XML 标签内。例如下面代码就不能匹配:

>>> print(re.search(r'\w+)>\w+(?P=tag)>', '

hello'))

None

这是由于前后两个标签不相同,因此不能匹配。

(4)、(?:exp):匹配 exp 表达式并且不捕获。这种组与 (exp) 的区别就在于它是不捕获的,因此不能通过 \1、\2等方式来引用。例如下面这行代码运行时会报错:

re.search(r'Windows (?:95|98|NT|2000)[\w ]+\1', 'Windows 98 publised in 98')

将上面代码中的 \1 去掉后,表示不捕获前面匹配的组,就可以正常匹配:

>>> re.search(r'Windows (?:95|98|NT|2000)[\w ]+', 'Windows 98 publised in 98')<_sre.sre_match object span="(0," match="Windows 98 publised in 98">

#如果不想匹配后面的数字,可将 [\w ]+ 部分修改为 [a-z ]+

>>> re.search(r'Windows (?:95|98|NT|2000)[a-z ]+', 'Windows 98 publised in 98')<_sre.sre_match object span="(0," match="Windows 98 publised in">

(5)、(?<=exp):括号中的子模式必须出现在匹配内容的左侧,但 exp 不作为匹配结果的一部分。

(6)、(?=exp):括号中的子模式必须出现在匹配内容的右侧,但 exp 不作为匹配结果的一部分。

上面两个子表达式主要用于对匹配内容进行限定,括号中的子模式本身不作为匹配的一部分。例如要获取 HMTL 代码中的标签的内容。

>>> re.search(r'(?<=).+?(?=)', 'hello! michael.com! technology')<_sre.sre_match object span="(10," match="michael.com">

在上面的正则表达式中 (?<=) 是一个限定组,该组的内容是 ,由于该组用了 (?<=exp) 声明,因此在被匹配内容的左侧必须有 ;后一个限定组是 (?=),该组的内容是 ,该组用了 (?=exp) 声明,因此要求在被匹配内容的右侧必须出现 。所以上面的正则表达式会将 和 之间的内容匹配出来。再例如:

>>> re.search(r'(?<=).+?(?=)', 'hello!

michael
! technology')<_sre.sre_match object span="(10," match="<div>michael</div>">

(7)、 (?

(8)、(?!exp):括号的子模式必须不出现在匹配内容的右侧,且 exp 不作为匹配的一部分。这个是 (?=exp) 的逆向表达。

(9)、(?#comment):注释组。“?#” 后的内容是注释,不影响正则表达式本身。示例如下:

>>> re.search(r'[a-zA-Z0-9_]{3,}(?#username)@michael\.com', 'hello stark@michael.com')<_sre.sre_match object span="(6," match="stark@michael.com">

上面代码中的 (?#username) 就是注释,对正则表达式不会有什么影响,只用于对部分内容进行说明。

(10)、(?aiLmsux):旗标组,用于为整个正则表达式添加行内旗标,可同时指定一个或多个旗标。示例如下:

>>> re.findall(r'(?im)[a-z0-9]{3,}@michael\.com', 'Stark@MICHAEL.COM,\

... Tom@michael.COM')

['Stark@MICHAEL.COM', 'Tom@michael.COM']

上面代码中的 (?im) 组表示该正则表达式在匹配时不区分大小写,并且可以匹配多行。所以匹配到多个结果,并以列表形式返回结果。如果将该旗标组去掉,就不能匹配到结果。

(11)、(?imsx-imsx:exp):只对当前组起作用的旗标。该组旗标与 (?aiLmsux) 组旗标的区别是,(?aiLmsux) 组旗标作用于整个正则表达式,而这组旗标只影响组内的子表达式。示例如下:

>>> re.search(r'(?i:[a-z0-9]){3,}@michael\.com', 'Stark@michael.com')<_sre.sre_match object span="(0," match="Stark@michael.com">

上面这个表达中的 (?i:[a-z0-9]) 组表示子表达式不区分大小写,但整个表达式依然区分大小写。因此这个表达式可以匹配 Stark@michael.com,但不能匹配 Stark@Michael.com,因为后面部分依然要区分大小写。

如果在旗标前使用减号 “-”,则表明去掉该旗标。比如在执行 search() 方法时传入了 re.I 参数,这表示整个正则表达式不区分大小写;如果希望某个组内的表达式依然区分大小,则可使用 (-i:exp) 来表示。例如:

>>> re.search(r'(?-i:[a-z0-9]){3,}@michael\.com', 'stark@Michael.Com', re.I)<_sre.sre_match object span="(0," match="stark@Michael.Com">

在 search() 方法中指定了 re.I 选项,表示整个正则表达式在匹配时不区分大小写;但是又要求用户名必须区分大小写,于是就把用户名部分放在用组定义成的子表达中,并为该子表达式指定 “?-i:” 选项(表明去除 re.I 选项),这样在组内的子表达式就会区分大小写。所在上面这个表达式可以匹配 stark@Michael.com、stark@michael.com,但不能匹配 Stark@Michael.com,因为用户名是区分大小写的。

5、贪婪模式与勉强模式

正则表达可以限定频度,用于限定前面的模式可以出现的次数。Python 正则表达式支持的频度限定有下面几种:

(1)、*:限定前面的子表达可以出现 0~N 次。例如 r'zo*' 能匹配 'z',也能匹配 'zoo'、'zooo'等。这里 * 等价于 {0,}。

(2)、+:限定前面的子表达可以出现 1~N 次。例如 r'zo+' 不能匹配 'z',可匹配 'zo'、'zoo'、'zooo'等。+ 等价于 {1,}。

(3)、?:限定前面的子表达出现 0~1 次。例如 r'zo?' 能匹配 'z' 和 'zo' 两个字符串。? 等价于 {0,1}。

(4)、{m,n}:m 和 n 均为大于 0 的整数,其中 m<=n,限定前面的子表达出现 m~n 次。例如 r'fo{1,3}d' 可匹配 'fod'、'food'、'foood'这三个字符串。

(5)、{n,}:n 是一个大于0的整数,限定前面的子表达式至少出现 n 次。例如 r'fo{2,}d' 可匹配 'food'、'foood'、'fooood'等字符串。

(6)、{,m}:m 是一个大于0的整数,限定前面的子表达最多出现 m 次。例如 r'fo{,3}d' 可匹配 'fd'、'fod'、'food'、'foood' 这四个字符串。

(7)、{n}:n 是一个大于0的整数,限定前面的子表达必须出现 n 次。例如 r'fo{2}d' 只能匹配 'food' 字符串。

在默认情况下,正则表达式的频度限定是贪婪模式的。贪婪模式指的是表达式中的模式会尽可能多的匹配字符。示例如下:

>>> re.search(r'@.+\.', 'stark@michael.com.cn')<_sre.sre_match object span="(5," match="@michael.com.">

上面的正则表达式 r'@.+\.' 是匹配 @ 符号和点(.)号之间的全部内容,希望匹配的结果是 “@michael.”。但是由于在@和点号之间用的是 “.+” 表示匹配任意字符,而且此时是贪婪模式,因此 “.+” 会尽可多的进行匹配,只要最后有一个 “.” 结尾即可,所以匹配到的结果是 “@michael.com.”

只要在频度限定之后添加一个英文问号,贪婪模式就变成了勉强模式,勉强模式指的是表达式中的模式会尽可能少的匹配字符。示例如下:

>>> re.search(r'@.+?\.', 'stark@michael.com.cn.')<_sre.sre_match object span="(5," match="@michael.">

这次将正则表达式中间部分由 “.+” 改为 “.+?” 就成了勉强模式。该模式会尽可能少的匹配字符,只要它最后有一个“.”结尾即可,因此匹配结果是 '@michael.'。

相关文章:

  • 可视化报表制作教程_聊聊大屏数据可视化
  • 塞尔达传说gba_《塞尔达传说》系列进化史 画面精美,探索内容更丰富
  • SQL SERVER DBCC命令解释
  • python 输出数据成行_python dataframe 输出结果整行显示的方法
  • python string indices must be_Python解析json时提示“string indices must be integers”问题解决方法...
  • SSIS:用foreach loop将一个目录下的内容导入到数据库中
  • 必须声明标量变量 @sum_level。_C++ Primer 笔记 - 变量
  • JAVA 中URL链接中文参数乱码的处理方法
  • 宝乐机器人集团作_「驰众机器人|情报」秦川集团起草的《机器人用精密摆线齿轮减速器》标准获发布...
  • java读取xml文件操作
  • 5ecsgo启动失败2错误代码2_Spring Boot 2.2 增加了一个新功能,启动飞起~
  • 现代科技概论_现代科技概论课程:力与运动3
  • 负载均衡技术全攻略
  • opencv图像分割python毕业设计_python用opencv完成图像分割并进行目标物的提取
  • SOA将使C++在2008年重现活力
  • conda常用的命令
  • PaddlePaddle-GitHub的正确打开姿势
  • react-native 安卓真机环境搭建
  • spring-boot List转Page
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • swift基础之_对象 实例方法 对象方法。
  • windows下使用nginx调试简介
  • 使用agvtool更改app version/build
  • 鱼骨图 - 如何绘制?
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​iOS安全加固方法及实现
  • ​人工智能书单(数学基础篇)
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #define
  • #NOIP 2014#Day.2 T3 解方程
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (c语言)strcpy函数用法
  • (k8s中)docker netty OOM问题记录
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • ..回顾17,展望18
  • ./和../以及/和~之间的区别
  • .NET Core 中的路径问题
  • .NET企业级应用架构设计系列之应用服务器
  • .php结尾的域名,【php】php正则截取url中域名后的内容
  • @Builder用法
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗
  • [C++]类和对象【上篇】
  • [cocos2d-x]关于CC_CALLBACK
  • [DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]
  • [HOW TO]怎么在iPhone程序中实现可多选可搜索按字母排序的联系人选择器
  • [IE9] 解决了傲游、搜狗浏览器在IE9下网页截图的问题
  • [iOS]如何删除工程里面用cocoapods导入的第三方库
  • [Java] 图说 注解
  • [JavaWeb]—Spring入门
  • [LeetCode][LCR178]训练计划 VI——使用位运算寻找数组中不同的数字