正则表达式是一组特殊的字符序列,帮助您匹配或查找其他字符串或一组字符串,使用模式中的专门语法。正则表达式通常被称为regex或regexp。
通常,这些模式被字符串搜索算法用于字符串上的“查找”或“查找并替换”操作,或者用于输入验证。
数据科学项目中的大规模文本处理需要对文本数据进行操纵。许多编程语言包括Python都支持正则表达式的处理。Python的标准库中有名为re
的模块用于此目的。
由于re
模块中定义的大多数函数都与原始字符串一起工作,让我们首先理解什么是原始字符串。
原始字符串
正则表达式使用反斜杠字符('')来指示特殊形式或允许特殊字符在没有调用它们特殊意义的情况下使用。另一方面,Python使用相同的字符作为转义字符。因此,Python使用原始字符串表示法。
如果一个字符串前面加上了r或R,则该字符串就变成了一个原始字符串。因此,'Hello'是一个普通的字符串,而r'Hello'是一个原始字符串。
>>> normal="Hello"
>>> print(normal)
Hello
>>> raw=r"Hello"
>>> print(raw)
Hello
在正常情况下,两者之间没有区别。然而,当转义字符嵌入到字符串中时,普通字符串实际上会解释转义序列,而原始字符串不会处理转义字符。
>>> normal="Hello\nWorld"
>>> print(normal)
Hello
World
>>> raw=r"Hello\nWorld"
>>> print(raw)
Hello\nWorld
在上面的例子中,当打印一个普通字符串时,转义字符\n
会被处理为引入换行符。然而,由于原始字符串运算符'r',转义字符的效果并没有按照其含义进行翻译。
元字符
大多数字母和字符只会匹配它们自己。然而,一些字符是特殊的元字符,并不匹配它们自己。元字符是指具有特殊意义的字符,类似于通配符中的*。
下面是元字符的完整列表:
. ^ $ * + ? { } [ ] \ | ( )
方括号符号 [
和 ]
表示一组您希望匹配的字符。字符可以单独列出,或者作为一个范围内的字符,以 -
分隔。
序号 |
元字符 |
描述 |
1 |
[abc] |
匹配 a, b 或者 c 中的任意一个 |
2 |
[a-c] |
使用范围来表达同样的字符集 |
3 |
[a-z] |
只匹配小写字母 |
4 |
[0-9] |
只匹配数字 |
5 |
^ |
互补方括号内的字符集 |
\'
是一个转义元字符。当跟随不同的字符时,它形成各种特殊序列。如果你需要匹配一个 [
或者 \
,可以在它们前面加上一个反斜杠来去除它们的特殊含义:\[
或者 \\
。
由这样的特殊序列开头的预定义字符集列于下表:
序号 |
元字符 |
描述 |
1 |
\d |
匹配任何十进制数字;等价于类 [0-9] |
2 |
\D |
匹配任何非数字字符;等价于类 [^0-9] |
3 |
\s |
匹配任何空白字符;等价于类 [\t\n\r\f\v] |
4 |
\S |
匹配任何非空白字符;等价于类 [^\t\n\r\f\v] |
5 |
\w |
匹配任何字母数字字符;等价于类 [a-zA-Z0-9_] |
6 |
\W |
匹配任何非字母数字字符;等价于类 [^a-zA-Z0-9_] |
7 |
. |
匹配除了换行符 \n 外的任何单个字符 |
8 |
? |
匹配左侧模式的 0 次或 1 次出现 |
9 |
+ |
左侧模式的 1 次或多次出现 |
10 |
* |
左侧模式的 0 次或多次出现 |
11 |
\b |
单词和非单词之间的边界 |
12 |
[..] |
匹配方括号中的任何一个字符 |
13 |
\ |
用于特殊意义的字符如 \. 匹配句点或 \+ 匹配加号 |
14 |
{n,m} |
匹配至少 n 次且最多 m 次的前导字符 |
15 |
`a |
b` |
Python的re
模块提供了有用的函数来查找匹配项、搜索模式以及用其他字符串替换匹配的字符串等。
re.match()
函数
此函数尝试在字符串开头匹配 RE 模式,并带有可选标志。以下是此函数的语法:
re.match(pattern, string, flags=0)
这里是对参数的描述:
序号 |
参数 |
描述 |
1 |
pattern |
要匹配的正则表达式 |
2 |
string |
将被搜索以在字符串开头匹配模式的字符串 |
3 |
flags |
您可以使用按位或 (` |
re.match()
函数在成功时返回一个匹配对象,在失败时返回 None
。匹配对象实例包含有关匹配的信息:它的开始位置和结束位置,它匹配的子字符串等。
如果未找到模式,匹配对象为 None
。
我们使用匹配对象的 group(num)
或 groups()
函数获取匹配的表达式。
序号 |
方法 |
描述 |
1 |
group(num=0) |
返回整个匹配(或特定子组 num) |
2 |
groups() |
以元组形式返回所有匹配的子组 |
示例
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'Cats', line)
print(matchObj.start(), matchObj.end())
print("matchObj.group() : ", matchObj.group())
它将产生以下输出:
0 4
matchObj.group() : Cats
re.search()
函数
此函数搜索字符串中首次出现的 RE 模式的匹配,带有可选标志。以下是此函数的语法:
re.search(pattern, string, flags=0)
这里是对参数的描述:
序号 |
参数 |
描述 |
1 |
pattern |
要匹配的正则表达式 |
2 |
string |
将被搜索以在字符串任意位置匹配模式的字符串 |
3 |
flags |
您可以使用按位或 (` |
re.search
函数在成功时返回一个匹配对象,在失败时返回 None
。我们使用匹配对象的 group(num)
或 groups()
函数获取匹配的表达式。
序号 |
方法 |
描述 |
1 |
group(num=0) |
返回整个匹配(或特定子组 num) |
2 |
groups() |
以元组形式返回所有匹配的子组 |
示例
import re
line = "Cats are smarter than dogs"
matchObj = re.search(r'than', line)
print(matchObj.start(), matchObj.end())
print("matchObj.group() : ", matchObj.group())
它将产生以下输出:
17 21
matchObj.group() : than
匹配 vs 搜索
Python 提供了两种基于正则表达式的不同原始操作:match
只检查字符串开头的匹配,而 search
在字符串的任何地方检查匹配(这默认是Perl所做的)。
示例
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'dogs', line, re.M|re.I)
if matchObj:
print("match --> matchObj.group() : ", matchObj.group())
else:
print("No match!!")
searchObj = re.search(r'dogs', line, re.M|re.I)
if searchObj:
print("search --> searchObj.group() : ", searchObj.group())
else:
print("Nothing found!!")
当上述代码被执行时,它产生以下输出:
No match!!
search --> matchObj.group() : dogs
re.findall()
函数
findall()
函数返回所有非重叠模式在字符串中的匹配,作为字符串列表或元组。字符串从左到右扫描,按发现的顺序返回匹配。空匹配包含在结果中。
语法
re.findall(pattern, string, flags=0)
参数
序号 |
参数 |
描述 |
1 |
pattern |
要匹配的正则表达式 |
2 |
string |
将被搜索以在字符串任意位置匹配模式的字符串 |
3 |
flags |
您可以使用按位或 (` |
示例
import re
string="Simple is better than complex."
obj=re.findall(r"ple", string)
print(obj)
它将产生以下输出:
['ple', 'ple']
下面的代码利用 findall()
函数获取句子中单词的列表。
import re
string="Simple is better than complex."
obj=re.findall(r"\w*", string)
print(obj)
它将产生以下输出:
['Simple', '', 'is', '', 'better', '', 'than', '', 'complex', '', '']
re.sub()
函数
使用正则表达式的最重要的 re
方法之一是 sub
。
语法
re.sub(pattern, repl, string, max=0)
此方法用 repl
替换字符串 string
中的所有 RE
模式出现,除非提供 max
,否则替换所有出现。此方法返回修改后的字符串。
示例
import re
phone = "2004-959-559 # This is Phone Number"
num = re.sub(r'#.*$', "", phone)
print("Phone Num : ", num)
num = re.sub(r'\D', "", phone)
print("Phone Num : ", num)
它将产生以下输出:
Phone Num : 2004-959-559
Phone Num : 2004959559
示例
下面的例子使用 sub()
函数将所有出现的 “is” 替换成 “was”:
import re
string="Simple is better than complex. Complex is better than complicated."
obj=re.sub(r'is', r'was', string)
print(obj)
它将产生以下输出:
Simple was better than complex. Complex was better than complicated.
re.compile()
函数
compile()
函数编译一个正则表达式模式为一个正则表达式对象,该对象可以用于匹配和其他方法,例如 match()
和 search()
。
语法
re.compile(pattern, flags=0)
标志
序号 |
修饰符 |
描述 |
1 |
re.I |
进行不区分大小写的匹配。 |
2 |
re.L |
根据当前区域设置来解释单词。这种解释影响字母组(\w 和 \W ),以及单词边界行为(\b 和 \B )。 |
3 |
re.M |
使 $ 匹配一行的结尾(不仅仅是字符串的结尾),并且使 ^ 匹配任何行的开头(不仅仅是字符串的开头)。 |
4 |
re.S |
使一个点(. )匹配任何字符,包括换行符。 |
5 |
re.U |
根据 Unicode 字符集来解释字母。此标志影响 \w , \W , \b , \B 的行为。 |
6 |
re.X |
允许“更可爱的”正则表达式语法。它忽略空白(除了在集合 [] 内部或被反斜杠转义的情况),并且把未转义的 # 当作注释标记。 |
模式序列:
prog = re.compile(pattern)
result = prog.match(string)
等价于:
result = re.match(pattern, string)
但是在单个程序中多次使用同一个表达式时,使用 re.compile()
并保存生成的正则表达式对象以便重复使用更为高效。
示例
import re
string="Simple is better than complex. Complex is better than complicated."
pattern=re.compile(r'is')
obj=pattern.match(string)
obj=pattern.search(string)
print(obj.start(), obj.end())
obj=pattern.findall(string)
print(obj)
obj=pattern.sub(r'was', string)
print(obj)
它将产生以下输出:
7 9
['is', 'is']
Simple was better than complex. Complex was better than complicated.
re.finditer()
函数
此函数返回一个迭代器,该迭代器在字符串中对所有不重叠的 RE 模式匹配返回匹配对象。
语法
re.finditer(pattern, string, flags=0)
示例
import re
string="Simple is better than complex. Complex is better than complicated."
pattern=re.compile(r'is')
iterator = pattern.finditer(string)
print(iterator)
for match in iterator:
print(match.span())
它将产生以下输出:
(7, 9)
(39, 41)
Python 正则表达式的使用案例
查找所有的副词
findall()
匹配一个模式的所有出现,不只是像 search()
那样只匹配第一个。例如,如果一位作家想要找出某段文字中所有的副词,他们可能会这样使用 findall()
:
import re
text = "He was carefully disguised but captured quickly by police."
obj = re.findall(r"\w+ly\b", text)
print(obj)
它将产生以下输出:
['carefully', 'quickly']
查找以元音开头的单词
import re
text = 'Errors should never pass silently. Unless explicitly silenced.'
obj=re.findall(r'\b[aeiouAEIOU]\w+', text)
print(obj)
它将产生以下输出:
['Errors', 'Unless', 'explicitly']
正则表达式修饰符:选项标志
正则表达式字面量可能包含一个可选的修饰符来控制匹配的各种方面。修饰符作为可选的标志指定。您可以使用异或(|
)提供多个修饰符,如下所示:
序号 |
修饰符 |
描述 |
1 |
re.I |
进行不区分大小写的匹配。 |
2 |
re.L |
根据当前区域设置来解释单词。这种解释影响字母组(\w 和 \W ),以及单词边界行为(\b 和 \B )。 |
3 |
re.M |
使 $ 匹配一行的结尾(不仅仅是字符串的结尾),并且使 ^ 匹配任何行的开头(不仅仅是字符串的开头)。 |
4 |
re.S |
使一个点(. )匹配任何字符,包括换行符。 |
5 |
re.U |
根据 Unicode 字符集来解释字母。此标志影响 \w , \W , \b , \B 的行为。 |
6 |
re.X |
允许“更可爱的”正则表达式语法。它忽略空白(除了在集合 [] 内部或被反斜杠转义的情况),并且把未转义的 # 当作注释标记。 |
正则表达式模式
除了控制字符(+ ? . * ^ $ ( ) [ ] { } | \
),所有字符都匹配它们自身。您可以通过在其前面加上反斜杠来转义一个控制字符。
下表列出了 Python 中可用的正则表达式语法:
序号 |
模式 |
描述 |
1 |
^ |
匹配行首。 |
2 |
$ |
匹配行尾。 |
3 |
. |
匹配除换行符外的任意单个字符。使用 m 选项允许匹配换行符。 |
4 |
[...] |
匹配括号内的任意单个字符。 |
5 |
[^...] |
匹配不在括号内的任意单个字符。 |
6 |
re* |
匹配前导表达式的 0 或更多次出现。 |
7 |
re+ |
匹配前导表达式的 1 次或更多次出现。 |
8 |
re? |
匹配前导表达式的 0 或 1 次出现。 |
9 |
re{n} |
匹配前导表达式恰好 n 次出现。 |
10 |
re{n,} |
匹配前导表达式至少 n 次出现。 |
11 |
re{n,m} |
匹配前导表达式至少 n 次但最多 m 次出现。 |
12 |
`a |
b` |
13 |
(re) |
组合正则表达式并记住匹配的文本。 |
14 |
(?imx) |
在正则表达式内临时启用 i , m , 或 x 选项。如果在括号内,则仅影响该区域。 |
15 |
(?-imx) |
在正则表达式内临时禁用 i , m , 或 x 选项。如果在括号内,则仅影响该区域。 |
16 |
(?:re) |
组合正则表达式而不记住匹配的文本。 |
17 |
(?imx:re) |
在括号内临时启用 i , m , 或 x 选项。 |
18 |
(?-imx:re) |
在括号内临时禁用 i , m , 或 x 选项。 |
19 |
(?#...) |
注释。 |
20 |
(?=re) |
使用模式指定位置。没有范围。 |
21 |
(?!re) |
使用模式否定指定位置。没有范围。 |
22 |
(?>re) |
不回溯地独立匹配模式。 |
23 |
\w |
匹配字母数字下划线字符。 |
24 |
\W |
匹配非字母数字下划线字符。 |
25 |
\s |
匹配空白字符。等同于 [\t\n\r\f] 。 |
26 |
\S |
匹配非空白字符。 |
27 |
\d |
匹配数字。等同于 [0-9] 。 |
28 |
\D |
匹配非数字。 |
29 |
\A |
匹配字符串开始。 |
30 |
\Z |
如果存在换行符,则匹配字符串结束之前的位置;否则匹配字符串末尾。 |
31 |
\z |
匹配字符串末尾。 |
32 |
\G |
匹配上一次匹配结束的位置。 |
33 |
\b |
当在括号之外时匹配单词边界;当在括号之内时匹配退格符(0x08)。 |
34 |
\B |
匹配非单词边界。 |
35 |
\n, \t, etc. |
匹配换行符、回车符、制表符等。 |
36 |
\1...\9 |
匹配第 n 个成组的子表达式。 |
37 |
\10 |
如果已经匹配过,则匹配第 10 个成组的子表达式。否则指代八进制形式的字符代码。 |
正则表达式示例
字面字符
序号 |
示例 |
描述 |
1 |
python |
匹配 python 。 |
字符类
序号 |
示例 |
描述 |
1 |
[Pp]ython |
匹配 Python 或 python 。 |
2 |
rub[ye] |
匹配 ruby 或 rube 。 |
3 |
[aeiou] |
匹配任意一个小写元音字母。 |
4 |
[0-9] |
匹配任何一个数字;等同于 [0123456789] 。 |
5 |
[a-z] |
匹配任何一个小写 ASCII 字母。 |
6 |
[A-Z] |
匹配任何一个大写 ASCII 字母。 |
7 |
[a-zA-Z0-9] |
匹配以上任何一个字符。 |
8 |
[^aeiou] |
匹配除了一个小写元音字母以外的任何字符。 |
9 |
[^0-9] |
匹配除了一个数字以外的任何字符。 |
特殊字符类
序号 |
示例 |
描述 |
1 |
. |
匹配除换行符外的任意字符。 |
2 |
\d |
匹配一个数字:[0-9] 。 |
3 |
\D |
匹配一个非数字:[^0-9] 。 |
4 |
\s |
匹配一个空白字符:[ \t\r\n\f] 。 |
5 |
\S |
匹配非空白:[^ \t\r\n\f] 。 |
6 |
\w |
匹配一个单词字符:[A-Za-z0-9_] 。 |
7 |
\W |
匹配一个非单词字符:[^A-Za-z0-9_] 。 |
重复情况
序号 |
示例 |
描述 |
1 |
ruby? |
匹配 rub 或 ruby :y 是可选的。 |
2 |
ruby* |
匹配 rub 加上 0 个或更多的 y 。 |
3 |
ruby+ |
匹配 rub 加上 1 个或更多的 y 。 |
4 |
\d{3} |
匹配正好 3 位数字。 |
5 |
\d{3,} |
匹配 3 位或更多位数字。 |
6 |
\d{3,5} |
匹配 3 到 5 位数字。 |
非贪婪重复
这匹配最少数量的重复:
序号 |
示例 |
描述 |
1 |
<.*> |
贪婪重复:匹配 <python>perl> 。 |
2 |
<.*?> |
非贪婪:在 <python>perl> 中匹配 <python> 。 |
用括号进行分组
序号 |
示例 |
描述 |
1 |
\D\d+ |
没有分组:+ 重复 \d 。 |
2 |
(\D\d)+ |
分组:+ 重复 \D\d 对。 |
3 |
([Pp]ython(, )?)+ |
匹配 Python , Python, python, python 等。 |
后向引用
这再次匹配先前匹配的组:
序号 |
示例 |
描述 |
1 |
([Pp])ython&\1ails |
匹配 python&pails 或 Python&Pails 。 |
2 |
(['"])[^\1]*\1 |
单引号或双引号内的字符串。\1 匹配第一个组匹配的内容。 |
备选项
序号 |
示例 |
描述 |
1 |
`python |
perl` |
2 |
`rub(y |
le))` |
3 |
`Python(!+ |
?)` |
锚定
这需要指定匹配位置:
序号 |
示例 |
描述 |
1 |
^Python |
匹配字符串或内部行首的 Python 。 |
2 |
Python$ |
匹配字符串或行尾的 Python 。 |
3 |
\APython |
匹配字符串开始的 Python 。 |
4 |
Python\Z |
匹配字符串末尾的 Python 。 |
5 |
\bPython\b |
匹配单词边界上的 Python 。 |
6 |
\brub\B |
\B 是非单词边界:匹配 rube 和 ruby 中的 rub 但不是单独的 rub 。 |
7 |
Python(?=!) |
匹配后面跟着感叹号的 Python 。 |
8 |
Python(?!!) |
匹配后面不跟着感叹号的 Python 。 |
括号中的特殊语法
序号 |
示例 |
描述 |
1 |
R(?#comment) |
匹配 R 。其余都是注释。 |
2 |
R(?i)uby |
在匹配 uby 时不区分大小写。 |
3 |
R(?i:uby) |
同上。 |
4 |
`rub(?:y |
le))` |