Logo

郎哥编程

入门正则表达式

2020-12-30 164

正则表达式可以使用一些预定义的字符、符号以及这些字符或符号的组合,组成一个规则串(规则串也称为模式串或正则式),然后使用这个规则串对需要处理的一段字符串进行匹配。如果这段字符串的内容和规则串能够匹配,则匹配成功,否则就匹配失败。正则表达式被经常使用在数据采集、搜索引擎、编译系统、文本编辑器等方面。

入门正则表达式

假如您要使用Python开发一个用户注册程序,需要验证用户输入的邮箱地址格式是否正确,该怎么编写这个程序呢?

要验证邮箱地址的格式是否正确,需要先弄清楚邮箱地址的格式规则,有了邮箱地址的格式规则,就可以编写正则表达式了。观察邮箱地址,发现每个邮箱地址都包含“@”字符。只有检测给出的邮箱地址是否包含“@”字符,就可以基本确定邮箱地址格式是否正确。如何判断一个字符串是否包含“@”字符呢?在Python语言中,可以使用成员运算符in来判断,不过在这里使用正则表达式来检测。

正则表达式就是一段字符串,这段字符串由一些预定义的字符、符号以及这些字符或符号组合而成。

先来认识预定义符号“[]”,这个符号是中括号,在中括号内可以包含一个或多个字符及符号构成的集合,当需要匹配的字符串包含中括号内的任意一个字符时,匹配就成功。

例如:匹配一个邮箱地址是否包含“@”符号,可以使用下面的正则表达式:

pattern = “[@]”

pattern是一个最简单的正则表达式,它使用了预定义的“[]”符号,在“[]”符号内包含一个“@”字符,使用该正则表达式可以匹配包含“@”字符的字符串,也可以用于检测邮箱地址是否包含“@”字符。

Python如何执行正则表达式?

Python提供了re模块用于执行正则表达式。re模块提供了两个主要的正则表达式执行函数,分别是match函数和search函数。match函数尝试从字符串的起始位置开始匹配,如果在起始位置匹配不成功的话,match函数返回None;search函数会扫描整个字符串,并返回第一个成功的匹配。

match函数和search函数都有三个相同的参数,参数如下表所示:

17.PNG

下面的代码使用match函数检测邮箱地址是否包含“@”字符,可以猜想一下,使用下面的代码能检测成功吗?

import re
# 正则表示式
pattern = "[@]"
# 使用re模块mach函数进行匹配
print(re.match(pattern, 'bianchen@163.com'))

上面的代码pattern是正则表达式,使用re模块的match函数匹配邮箱地址bianchen@163.com,验证邮箱地址是否包含“@”字符。前面说过,match函数尝试从字符串的起始位置开始匹配,如果在起始位置匹配不成功的话,match函数返回None。

再看下面的代码:

import re
# 正则表示式
pattern = "[@]"
# 使用re模块search函数进行查找
print(re.search(pattern, 'bianchen@163.com'))

在上面的代码中,使用了re模块的search函数。前面说过,search函数会扫描整个字符串,并返回第一个成功的匹配。因此使用search函数会匹配成功。执行结果如下所示:

<re.Match object; span=(8, 9), match='@'>

search函数匹配成功返回的结果是一个Match对象,Match对象提供了一些方法,以不同方式来获取匹配结果,后面会讲到Match对象。

search函数主要是应用正则表达式进行字符串查找操作,并不适合做验证邮箱地址格式的工作,我们还是要使用match函数进行邮箱地址格式的验证工作。

观察下面的邮箱地址:

bianchen@163.com
johy_1996820@yahoo.com
89299001@qq.com

发现每个邮箱地址都符合“名称@域名”规则,字符“@”在邮箱地址的“名称”和“域名”之间。进一步观察发现,邮箱地址名称只允许由英文字母、数字、下划线组成;域名只允许由英文字母、数字、下划线、“.”组成。

基于上述观察,在写正则表达式时,可以在符号“@”前面匹配任意多个符合邮箱名称规则的字符和符号,在符号“@”后面匹配任意多个符合邮箱域名规则的字符和符号。

下面给出的正则表达式可以验证邮箱地址格式的正确性:

pattern = "\w+[@][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)"

这段正则表达式比前面的正则表达式复杂了许多,是不是感觉不太容易理解,这一长串字符和符号的组合都有什么含义呢?

首先,我们以字符串“[@]”为分隔串将正则表达式分为两部分。

第一部分是:

“\w+”

第二部分是:

“[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)”

第一部分是匹配邮箱名称,前面说过邮箱名称由英文字母、数字、下划线组成。在正则表达式中,使用“\w”来匹配数字、字母和下划线,“\W”来匹配非数字、字母和下划线。

类似于“\w”、“\W”这种字符组合在正则表达式中也称为元字符,前面介绍的“[]”也是正则表达式的元字符,元字符使正则表达式具有处理能力。例如:“\w”元字符可以让正则表达式具备匹配数字、字母和下划线的能力。

元字符“\w”仅匹配单个数字、字母和下划线,如果需要匹配多个数字、字母和下划线,就需要用到正则表达式的元字符“+”,元字符“+”可以让前面的字符或子表达式匹配一次或多次。例如:“\w+”子表达式就具备匹配一个或多个数字、字母和下划线的能力。

第二部分是匹配邮箱域名,邮箱域名由英文字母、数字、下划线和“.”组成,邮箱域名分为前缀和后缀两部分,在前缀和后缀之间用“.”分隔。 “[a-zA-Z0-9_-]”表示匹配’a’-‘z’范围内的小写字母、’A’-‘Z’范围内的大写字母、数字0-9、下划线。“[a-zA-Z0-9_-]”等同于元字符“\w”。“[a-zA-Z0-9_-]+”等同于“\w+”。

“(\.[a-zA-Z0-9_]+)”是子表达式,子表达式内容使用一对小括号括起来,一对小括号也是正则表达式的元字符。括号内的“\.”用于匹配邮箱域名的“.”,其中元字符“\”是转义元字符,它把元字符后面的字符标记为特殊字符、文本等。例如:“\.”匹配单符号“.”,“\n”匹配单字符“n”。“[a-zA-Z0-9_]+”就不用解释了。

import re
# 正则表示式
pattern = "\w+[@][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)"
# 使用re模块mathc函数进行匹配
print(re.match(pattern, 'bianchen@163.com'))

程序执行结果如下所示:

<re.Match object; span=(0, 16), match='bianchen@163.com'>

判断邮箱地址格式还有点小问题,修改一下上面的代码:

import re
# 正则表示式
pattern = "\w+[@][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)"
# 使用re模块mathc函数进行匹配
print(re.match(pattern, 'bianchen@wy.163.com'))

在上面的代码中,需要验证的邮箱地址域名分为一级和二级域名,执行程序看上面的代码是否还能匹配成功。执行结果如下所示:

<re.Match object; span=(0, 15), match='bianchen@wy.163'>

从执行结果可以看出,邮箱地址并没有完全匹配。问题在于“(\.[a-zA-Z0-9_]+)”仅匹配一次域名的后缀,如果邮箱域名有多个域名后缀则只能匹配最前面一个。要解决这个问题,就需要“(\.[a-zA-Z0-9_]+)”重复匹配一次或多次,前面说过,正则表达式的元字符“+”可以让前面的字符或子表达式匹配一次或多次,在“(\.[a-zA-Z0-9_]+)”后面添加元字符“+”,就可以实现重复匹配一次或多次。修改上面的代码:

import re
# 正则表示式
pattern = "\w+[@][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+"
# 使用re模块mathc函数进行匹配
print(re.match(pattern, 'bianchen@wy.163.com'))

程序执行结果如下所示:

<re.Match object; span=(0, 19), match='bianchen@wy.163.com'>

正则表达式元字符

.

元字符“.”匹配除了换行符以外的的任意字符。如果指定了标签 DOTALL ,它将匹配包括换行符的任意字符。

^

元字符“^”匹配字符串的首个符号。如果设置了MULTILINE模式,也匹配换行后的首个符号。

$

元字符”$” 匹配字符串尾或者换行符的前一个字符。如果设置了 MULTILINE 模式匹配换行符的前一个字符。例如:foo 匹配 'foo' 和 'foobar' , 但正则 foo$ 只匹配 'foo'。

*

元字符”*” 对它前面的正则式匹配0到任意次重复, 尽量多的匹配字符串。例如:ab* 会匹配 'a', 'ab', 或者 'a'后面跟随任意个 'b'。

+

元字符“+”对它前面的正则式匹配1到任意次重复。例如: ab+ 会匹配 'a' 后面跟随1个以上到任意个 'b',它不会匹配 'a'。

?

元字符“?” 对它前面的正则式匹配0到1次重复。例如:ab? 会匹配 'a' 或者 'ab'。

*?, +?, ??

元字符'*', '+',和 '?' 都是贪婪的,它们在字符串进行尽可能多的匹配。

有时候并不需要这种行为。例如:正则式 <.*> 希望匹配 '<a> b <c>'的‘<a>’,它将会匹配整个字符串,而不仅是 '<a>'。在修饰符之后添加 ? 将使正则式以 非贪婪`方式或者最小方式进行匹配。 使用正则式 <.*?> 将会仅仅匹配 '<a>'。

{m}

元字符“{m}” 对其之前的正则式指定匹配 m 个重复;少于 m 的话就会导致匹配失败。例如: a{6} 将匹配6个 'a' , 但是不能是5个。

{m,n}

元字符“{m,n}” 对正则式进行 m 到 n 次匹配,在 m 和 n 之间取尽量多。

例如: a{3,5} 将匹配 3 到 5个 'a'。忽略 m 意为指定下界为0,忽略 n 指定上界为无限次。 例如:a{4,}b 将匹配 'aaaab' 或者1000个 'a' 尾随一个 'b',但不能匹配 'aaab'。可以忽略m或n,但逗号不能省略。

{m,n}?

元字符“{m,n}?” 是对{m,n}的非贪婪模式,只匹配尽量少的字符次数。

例如:对于 'aaaaaa', a{3,5} 匹配 5个 'a' ,而 a{3,5}? 只匹配3个 'a'。

\

元字符“\” 把该元字符后面的字符标记为特殊字符、文本等。

例如:例如:“\.”匹配单符号“.”,“\n”匹配单字符“n”、“\\n” 匹配换行符。

[]

元字符“[]” 把需要匹配的字符、数字、符号放置在“[]”内,正则会匹配“[]”内的任意一个字符、数字或符号。

(1)   “[]”内的字符可任意列出。例如:[amk] 匹配 'a', 'm', 或者 'k'。

(2)   “[]”内的字符可以是字符范围,字符范围通过通过用 '-' 将两个字符连起来。例如: [a-z] 将匹配任何小写ASCII字符, [0-5][0-9] 将匹配从 00 到 59 的两位数字, [0-9A-Fa-f] 将匹配任何十六进制数位。

(3)“[]”内要匹配一个字符 ']',可以在它之前加上反斜杠。例如:[()\]{}]。 (...)

用于标记一个子正则式,子表达式的内容放置在小括号内。在Python中也称为组合,匹配完成后,组合的内容可以被获取。

例如:(ab?)就是一个子正则式或组合。

(?P<name>…)

命名的正则式组合,但是匹配到的子串组在外部是通过定义的 name 来获取的。组合名必须是有效的Python标识符,并且每个组合名只能用一个正则表达式定义,只能定义一次。

(?P=name)

在正则式中引用一个命名组合;它匹配前面那个叫 name 的命名组中匹配到的串同样的字串。

(?#…)

正则式内的注释,里面的内容会被忽略。

(?=…)

匹配括号内的…的内容。例如:Isaac (?=Asimov) 匹配 'Isaac ',只有在后面是 'Asimov' 的时候。

(?!…)

匹配 … 不符合的情况。例如:Isaac (?!Asimov) 只有后面 不 是 'Asimov' 的时候才匹配 'Isaac ' 。

(?<=…)

匹配字符串的当前位置,它的前面匹配 … 的内容到当前位置。

例如:(?<=abc)def 会在 'abcdef' 中找到一个匹配,因为后视会往后看3个字符并检查是否包含匹配的模式串。

包含的匹配模式串必须是定长的,意思就是 abc 或 a|b 是允许的,但是 a* 和 a{3,4} 不可以。(?<=abc)def ,并不是从 a 开始搜索,而是从 d 往回看的。该正则式主要用于 search() 函数。

\number

元字符“\number” 匹配数字代表的组合,每个括号是一个组合,组合从1开始编号。

例如: (.+) \1 匹配 'the the' 或者 '55 55', 但不会匹配 'thethe' (注意组合后面的空格)。

\A

元字符“\A” 只匹配字符串开始。

\b

元字符”\b” 匹配空字符串,但只在单词开始或结尾的位置。例如:'\bfoo\b' 匹配 'foo', 'foo.', '(foo)', 'bar foo baz' 但不匹配 'foobar' 或者 'foo3'。

\B

元字符“\B” 匹配空字符串,但不能在词的开头或者结尾。例如:'py\B' 匹配 'python', 'py3', 'py2', 但不匹配 'py', 'py.', 或者 'py!'。

\d

元字符“\d” 匹配任何Unicode十进制数, 如果设置了 ASCII 标志,就只匹配 [0-9]。

\D

元字符“\D” 匹配任何非十进制数字的字符。如果设置了 ASCII 标志,就相当于 [^0-9]。

\s

元字符“\s” 对于 Unicode字符串,匹配任何空白字符。如果 ASCII 被设置,就只匹配 [ \t\n\r\f\v]。

\S

元字符“\S” 匹配任何非空白字符。如果设置了 ASCII 标志,就相当于 [^ \t\n\r\f\v] 。

\w

元字符“\w” 匹配Unicode词语的字符,包含了可以构成词语的绝大部分字符,也包括数字和下划线。如果设置了 ASCII 标志,则匹配 [a-zA-Z0-9_] 。

\W

元字符“\W” 匹配非单词字符的字符。这与 \w 正相反。如果设置了 ASCII 标志,等价于 [^a-zA-Z0-9_]。

\Z

元字符“\Z” 只匹配字符串尾。

 正则表达式标志

re模块定义了一些正则标志,用来设置正则表达式的匹配特性。例如:设置被匹配的字符串是Unicode编码,还是ASCII编码。

re.A
re.ASCII

标志A和ASCII等价,让 \w, \W, \b, \B, \d, \D, \s 和 \S 元字符只匹配ASCII,而不是Unicode。

re.M
re.MULTILINE

标志M和MULTILINE等价,设置以后,样式字符 '^' 匹配字符串的开始,和每一行的开始(换行符后面紧跟的符号);样式字符 '$' 匹配字符串尾,和每一行的结尾(换行符前面那个符号)。默认情况下,’^’ 匹配字符串头,'$' 匹配字符串尾。
re.S
re.DOTALL

标志S和DOTALL等价,让元字符 '.' 匹配任何字符,包括换行符;如果没有这个标记,'.' 就匹配 除了 换行符的其他任意字符。

re.X
re.VERBOSE

标志X和VERBOSE等价,这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。

原始字符串标记

当在正则表达式需要匹配一个反斜杠“\”字符时,它必须在正则表达式中进行转义,需要须用 "\\\\"来表示一个反斜杠“\”字符。

原始字符串标记,就是在字符串前面加一个小写字母r,r不包含在字符串内。例如: r"\\"表示一个反斜杠“\”字符。

下面的两个正则表达式是同样的意思:

>>> import re
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>

 

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论