一、正则表达式简介

正则表达式(RegExp)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(元字符)。通常用于在给定字符串中匹配满足表达式规则的子串,以供进一步处理。

譬如正则表达式 'a' 可以匹配字符串 'I have a dream!' 中所有的字符 'a',检索之后可以进行替换操作,如将 'a' 替换为 'b'

print(re.sub('a', 'b', 'I have a dream!'))
# 结果为
I hbve b drebm!

1、运算符的优先级

正则表达式运算符的优先级顺序由高到低依次为:

  • 转义符:\
  • 括号和中括号:()(?:)(?=)[]
  • 限定符:*+?{n}{n,}{n,m}
  • 定位点和序列:^$\元字符字符
  • 替换:|

字符具有高于替换运算符的优先级,使得 m|food 匹配 m 或 food。若要匹配 mood 或 food,请使用括号创建子表达式,即 (m|f)ood

2、常用验证规则

匹配汉字[\u4e00-\u9fa5]

二、re 模块简介

re 模块提供了 Perl 风格的正则表达式模式。Perl 5 对标准正则表达式增加了几个附加功能,re 模块也支持其中的大部分。

1、Raw String

正则表达式使用反斜杠 \ 来代表特殊形式或用作转义字符,这里跟 Python 的语法冲突,因此,Python 只好用 \\\\ 匹配 \,因为正则表达式中如果要匹配 \,需要用 \ 来转义,变成 \\,而 Python 语法中又需要对字符串中每一个 \ 进行转义,所以就变成了 \\\\

为了使正则表达式具有更好的可读性,Python 特别设计了 Raw String。Raw String 以 r 作为字符串的前缀,如 r"\n" 表示字符 \ 和 n。

  • 并非所有的正则表达式都需要使用 Raw String,但 compile 方法必须以 r 作为字符串的前缀;
  • 对于其他方法(如 match、search 等 )而言,是否以 r 作为字符串的前缀并不影响结果。

2、正则表达式对象

compile(RegExp [, flags]) 可以把正则表达式编译成一个正则表达式对象。其中,RegExp 为正则表达式,flags 为编译标志。

import re
html = re.compile(r"<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*/\1\s*>") # 匹配 HTML 标记(1)

3、编译标志

编译标志控制表达式的匹配方式。多个标志可以通过 | 来指定,如 re.I | re.M 被设置成 I 和 M 标志。

标志 含义
S 或 DOTALL 使 . 匹配包括换行在内的所有字符
I 或 IGNORECASE 使匹配对大小写不敏感
L 或 LOCALE 做本地化识别匹配
M 或 MULTILINE 多行匹配,影响 ^$
X 或 VERBOSE 提高正则表达式的可读性

X 标志的作用:

  • 不在字符集中的空白字符将被忽略。这使得:dog | cat 和可读性差的 dog|cat 相同,但 [a b] 将匹配字符 a、b 或空格。
  • 可以把注释放到正则表达式当中。注释从 # 开始到行末结束。
Xhtml = re.compile(r'''# 匹配 HTML 标记(2)
<\s*(\S+)(\s[^>]*)?>   # 开始标签
[\s\S]*                # 标签内的文本
<\s*/\1\s*>            # 同名结束标签 
''', re.X)             # 同样匹配 HTML 标记,方法(2)的可读性比(1)高了很多。

4、执行匹配

方法 用途
match(RegExp, string [, flags]) 从字符串的开始匹配一个模式,成功则返回 MatchObject 实例,否则返回 None
search(RegExp, string [, flags]) 在整个字符串内查找模式匹配,成功则返回 MatchObject 实例,否则返回 None
findall(RegExp, string [, flags]) 获取所有匹配的子串,并把它们作为一个列表返回
finditer(RegExp, string [, flags]) 获取所有匹配的子串,并把它们作为一个迭代器返回

若已将正则表达式 RegExp 编译成了正则表达式对象 RegPat,还可以使用 RegPat.match(string) 执行匹配。

string = '''Hello World!
<ul class="nav">
	<li class="inactive"> 云 </li>
	<li class="inactive"> 大数据 </li>
</ul>
'''

match_1 = re.match("\s\S*",string)
match_2 = re.match("\S*",string)
search_1 = re.search("\s\S*",string)
search_2 = Xhtml.search(string)

print(match_1, "\n", match_2, end = "\n-------------\n ")
print(search_1, "\n", search_2)

None 
 <_sre.SRE_Match object; span=(0, 5), match='Hello'>
-------------
 <_sre.SRE_Match object; span=(5, 12), match=' World!'> 
 <_sre.SRE_Match object; span=(13, 99), match='<ul class="nav">\n\t<li class="inactive"> 云 </li>>

5、MatchObject 实例

方法 用途
group() 返回匹配的字符串
start() 返回匹配开始的位置
end() 返回匹配结束的位置
span() 返回一个二元元组: (开始位置,结束位置)
print(search_2.group())

<ul class="nav">
	<li class="inactive">  </li>
	<li class="inactive"> 大数据 </li>
</ul>

6、操作字符串

方法 用途
split(RegExp, string [, maxsplit = 0]) 将字符串在匹配的位置分片,并生成一个列表;若 maxsplit 非零,则只能得到 maxsplit 个分片
sub(RegExp, replace, string[, count = 0]) 找到所有匹配的子串,并用其它的字符串替换;若 count 非零,则最多执行 count 次替换
subn(RegExp, replace, string) 与 sub 类似,但返回二元元组:(新的字符串,执行替换的次数)
print(re.split("\n",string))                                 # 用回车符分片
print(re.sub("\t", lambda m: '[' + m.group() + ']', string)) # 将制表符用 [ ] 括起来

['Hello World!', '<ul class="nav">', '\t<li class="inactive"> 云 </li>', '\t<li class="inactive"> 大数据 </li>', '</ul>', '']
Hello World!
<ul class="nav">
[	]<li class="inactive">  </li>
[	]<li class="inactive"> 大数据 </li>
</ul>

三、应用案例

已知故障树结构函数的最简与-或表达式

式中,$X_i$ 为故障树中第 $i$ 个基本事件,$k$ 为故障树包含的最小割集数。如:

其中第 $i$ 个基本事件的结构重要度系数可以表示为

式中,$m$ 为包含该基本事件的最小割集数,$R_j$ 为包含该基本事件的第 $j$ 个最小割集中的基本事件数。

from fractions import Fraction
import re
def t2i(string):
	pattern = re.split("=",string)
	cuts = re.split("(?:\+|;)",pattern[len(pattern)-1])
	for i in range(len(cuts)):
		cuts[i] = re.findall("\d+",cuts[i])
	k = len(cuts)
	x = sorted([int(i) for i in set(re.findall("\d+",pattern[len(pattern)-1]))])
	I = []
	for i in x:
		m = 0
		for j in cuts:
			if str(i) in j:
				m += Fraction(1, len(j))
		I += [Fraction(1, k)*m]
	print(pattern[0],': ',end='')
	for i in range(len(x)):
		print('I' + str(x[i]) + ' =', I[i], '  ',end='')
	print('')

t2i('T_1=X_1+X_4+X_5+X_6 X_14+X_6 X_15+X_6 X_16+X_7+X_8+X_9+X_10+X_11+X_13+X_12+X_2+X_3')
t2i('T_2=[1;2;3;4;5;6;7;8;9;10;11;12;13;14;15,16;17;18;19;20;21;22;23;24;25;26;27;28,29,30,31;32;33;34;35;36;37;38;39;40;41;42;43;44]')
t2i('T_3=X_2+X_3+X_5+X_6+X_7 X_8+X_8 X_11+X_9 X_10+X_1 X_12 X_4')

# 运行结果:

T_1 : I1 = 1/15   I2 = 1/15   I3 = 1/15   I4 = 1/15   I5 = 1/15   I6 = 1/10   I7 = 1/15   I8 = 1/15   I9 = 1/15   I10 = 1/15   I11 = 1/15   I12 = 1/15   I13 = 1/15   I14 = 1/30   I15 = 1/30   I16 = 1/30   
T_2 : I1 = 1/40   I2 = 1/40   I3 = 1/40   I4 = 1/40   I5 = 1/40   I6 = 1/40   I7 = 1/40   I8 = 1/40   I9 = 1/40   I10 = 1/40   I11 = 1/40   I12 = 1/40   I13 = 1/40   I14 = 1/40   I15 = 1/80   I16 = 1/80   I17 = 1/40   I18 = 1/40   I19 = 1/40   I20 = 1/40   I21 = 1/40   I22 = 1/40   I23 = 1/40   I24 = 1/40   I25 = 1/40   I26 = 1/40   I27 = 1/40   I28 = 1/160   I29 = 1/160   I30 = 1/160   I31 = 1/160   I32 = 1/40   I33 = 1/40   I34 = 1/40   I35 = 1/40   I36 = 1/40   I37 = 1/40   I38 = 1/40   I39 = 1/40   I40 = 1/40   I41 = 1/40   I42 = 1/40   I43 = 1/40   I44 = 1/40   
T_3 : I1 = 1/24   I2 = 1/8   I3 = 1/8   I4 = 1/24   I5 = 1/8   I6 = 1/8   I7 = 1/16   I8 = 1/8   I9 = 1/16   I10 = 1/16   I11 = 1/16   I12 = 1/24

四、正则表达式的基本语法

\:将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, n 匹配字符 n, \n 匹配 \n。序列 \\ 匹配 \ , \( 匹配 ( 。

^:匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。

$:匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。

*:零次或多次匹配前面的字符或子表达式。例如,zo* 匹配 z 和 zoo 。* 等效于 {0,}

+:一次或多次匹配前面的字符或子表达式。例如, zo+ 与 zo 和 zoo 匹配,但与 z 不匹配。+ 等效于 {1,}

?:零次或一次匹配前面的字符或子表达式。例如, do(es)? 匹配 do 或 does 中的 do 。? 等效于 {0,1}

? 紧随任何其他限定符(*+?{n}{n,}{n,m})之后时,匹配模式是非贪心的。非贪心的模式匹配搜索到的,尽可能短的字符串,而默认的贪心模式匹配搜索到的,尽可能长的字符串。例如,在字符串 oooo 中, o+? 只匹配单个 o,而 o+ 匹配所有 o。

.:匹配除 \n 之外的任何单个字符。若要匹配包括 \n 在内的任意字符,请使用诸如 [\s\S] 之类的模式。

x|y:匹配 x 或 y。例如,z|food 匹配 z 或 food 。(z|f)ood 匹配 zood 或 food 。

{n}:n 是非负整数。正好匹配 n 次。例如, o{2} 与 Bob 中的 o 不匹配,但与 food 中的两个 o 匹配。

{n,}:n 是非负整数。至少匹配 n 次。例如, o{2,} 不匹配 Bob 中的 o ,而匹配 food 中的所有 o。 o{1,}o{0,} 分别等效于 o+o*

{n,m}:M 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如, o{1,3} 匹配 fooooood 中的头三个 o。o{0,1} 等效于 o?

[xyz]:字符集。匹配包含的任一字符。例如, [abc] 匹配 plain 中的 a 。

[^xyz]:反向字符集。匹配未包含的任何字符。例如, [^abc] 匹配 plain 中的 p 。

[a-z]:字符范围。匹配指定范围内的任何字符。例如, [a-z] 匹配 a 到 z 范围内的任何小写字母。

[^a-z]:反向范围字符。匹配不在指定的范围内的任何字符。例如, [^a-z] 匹配任何不在 a 到 z 范围内的任何字符。

(pattern):匹配 pattern 并捕获该匹配的子表达式。可以使用 \num 对捕获子表达式进行反向引用。括号 ( ) 使用 \( 或者 \) 匹配 。

(?:pattern):匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用 | 组合模式部件的情况很有用。例如,industr(?:y|ies) 是比 industry|industries 更经济的表达式。

(?=pattern):执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,Windows (?=95|98|NT|2000) 匹配 Windows 2000 中的 Windows ,但不匹配 Windows 3.1 中的 Windows 。

预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

(?!pattern):执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,Windows (?!95|98|NT|2000) 匹配 Windows 3.1 中的 Windows ,但不匹配 Windows 2000 中的 Windows 。

\b:匹配一个字边界,即字与空格间的位置。例如, er\b 匹配 never 中的 er ,但不匹配 verb 中的 er 。

\B:非字边界匹配。 er\B 匹配 verb 中的 er ,但不匹配 never 中的 er 。

\cx:匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。否则假定 \c 就是 c 字符本身。

\d:数字字符匹配。等效于 [0-9]

\D:非数字字符匹配。等效于 [^0-9]

\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 等效。

\w:匹配任何字类字符,包括下划线。与 [A-Za-z0-9_] 等效。

\W:与任何非单词字符匹配。与 [^A-Za-z0-9_] 等效。

\xn:匹配一个十六进制转义码。十六进制转义码必须是两位数长。例如, \x41 匹配 A 。允许在正则表达式中使用 ASCII 代码。

\num:匹配第 num 个捕获子表达式的反向引用。例如, (.)\1 匹配两个连续的相同字符。

\n:如果 \n 前面至少有 n 个捕获子表达式,那么是反向引用。如果 n 是八进制数 0-7,那么是八进制转义码。

\nm:如果 \nm 前面至少有 nm 个捕获子表达式,那么是反向引用。如果 \nm 前面至少有 n 个捕获,则是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 nm 是八进制数 0-7。

\nml:当 n 是八进制数 0-3,ml 是八进制数 0-7 时,匹配八进制转义码 nml。

\un:匹配以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 ©。

如果您喜欢这篇文章,欢迎转载、演绎或用于商业目的,但请务必保留作者署名以及本文链接!
Copyright © Pandaman