TUTORIAL

Python 正则表达式教程

Python 正则表达式模块教程

本页内容

在本教程中,您将学习正则表达式以及 Python 的 re 模块中定义的正则表达式操作。re 是 Python 的标准库,支持正则表达式的匹配操作。

Python 中的正则表达式是一组字符或序列,使用形式化语法将字符串与某个模式进行匹配。您可以把正则表达式看作一种嵌入在 Python 中的小型编程语言。

您可以使用正则表达式定义一些规则,然后用这些规则从给定的字符串中生成可能的字符串,与模式进行匹配。Python 中的正则表达式会被解释为一组指令。

Python 正则表达式 re.match() 函数

您可以使用 match 函数将正则表达式模式与给定的字符串进行匹配。match 函数包含标志(flags)。标志定义了正则表达式的行为,可以取不同的值,您将在本教程后面看到它们。

下面是 Python 中 match 函数的语法:

re.match(pattern, string, flags)

它有三个参数:

  1. pattern 是要匹配的正则表达式模式
  2. string 是要与正则表达式匹配的给定字符串
  3. flags 用于改变正则表达式的行为,是可选的。

如果匹配成功,将返回 match 对象,否则返回 None。match 对象还有两个主要方法,即 group(num) 和 group() 函数。使用这些函数的主要目的是分别返回匹配结果或某个特定的子序列以及所有子序列。

使用 re.match 函数

下面的例子演示了如何使用 match 函数:

import re

strTest = "Hello Python Programming"
mobj = re.match(r"hello", strTest, re.I)
print(mobj.group())

在这段代码中,首先导入 re 模块。然后将字符串 strTest 与正则表达式模式进行比较,match 函数返回的值将赋给 mobj。match 函数通过 re 调用,括号中的第一个参数是要匹配的模式,接着是要从中匹配模式的给定字符串,还会传入一个标志值。这里的 re.I 是标志值,表示 IGNORECASE(忽略大小写),因此无论模式和字符串的字母大小写是否不同都会被忽略(无论是大写还是小写字母)。

输出为:

Hello

在这个例子中,使用了前缀 r,表示该字符串是原始字符串。在原始字符串中,使用转义序列时不需要写双斜杠,例如,如果您想要一个反斜杠,只需写单个 \,而不是像在普通字符串中那样写双反斜杠 \\。这是普通字符串和原始字符串之间唯一的区别。

对普通字符串使用 re.match 函数

请看下面的例子,其中使用的是普通字符串而不是原始字符串:

import re

str = "\\tHello Python Programming"
mobj = re.match("\\thello", str, re.I)  # no match

str = "\tHello Python Programming"
mobj = re.match("\\thello", str, re.I)  # \thello is matching

Python 正则表达式 re.search() 函数

您可以使用 re.search() 函数在给定的字符串中搜索正则表达式模式。search 函数包含三个参数,分别是 pattern、给定的 string 和 flags(可选)。

下面是 Python 中 search 函数的语法:

re.search(pattern, string, flags)

下面的 Python 代码演示了 search() 函数的用法:

import re

str = "Hello Python Programming"
sobj = re.search(r"programming", str, re.I)
print(sobj.group())
Programming

在这段代码中,搜索的是单词 programming。search 函数会在整个字符串中搜索。search 和 match 的区别在于,match 函数只检查字符串的开头,而 search 会在整个字符串中搜索。

使用 re.search 在开头搜索

如果您想在字符串的开头进行搜索,可以使用 ^。请看下面的例子:

import re

str = "Hello Python Programming"
sobj = re.search(r"^programming", str, re.I)
print(sobj.group())  # no match is found

sobj = re.search(r"^hello", str, re.I)
print(sobj.group())  # matching: Hello

在这里,^ 会让搜索只在字符串的开头进行。

使用 re.search 在末尾搜索

您也可以在给定字符串的末尾进行搜索。在模式的末尾使用 $ 即可做到这一点。请看下面的代码:

import re

str = "Hello Python Programming"
sobj = re.search(r"programming$", str, re.I)
print(sobj.group())  # matching: Programming

sobj = re.search(r"hello$", str, re.I)
print(sobj.group())  # no match found

使用 re.complie 编译正则表达式

Python 中的正则表达式在编译后会被转换成模式。这些模式实际上是模式对象,其中包含执行不同任务的各种函数,可能包括搜索、匹配和替换等。

当您编译了一个模式后,就可以在程序的后面使用这个模式。

使用预编译模式

请看下面的代码,其中编译了模式 r"\d",它表示字符串中的第一个数字,然后使用这个模式调用 search 函数,并向 search 函数传入一个字符串。这个模式将在提供给 search 函数的字符串中搜索。同样,您也可以按如下方式将这个预编译模式与 match 函数一起使用:

import re

compPat = re.compile(r"(\d)")
sobj = compPat.search("Lalalala 123")
print(mobj.group())

mobj = compPat.match("234Lalalala 123456789")
print(mobj.group())
1
2

Python 正则表达式 re 模块中的标志

您可以使用标志(Flags)来改变正则表达式的行为。在函数中,标志是可选的。您可以通过两种不同的方式使用标志:使用关键字 flags 并为其赋一个标志值,或者直接写出标志的值。在正则表达式字面量中可以有多个标志值,这可以通过按位 OR 运算符 | 来实现。

请看下表,其中用正则表达式字面量描述了一些常用的标志:

标志值 描述
re.I 此修饰符将在匹配时忽略字符串和模式的大小写。
re.L 此修饰符用于根据当前区域设置解释单词。
re.M 此修饰符用于让 $ 匹配行的末尾而不是字符串的末尾。同样,^ 将匹配行的开头而不是字符串的开头。
re.S 此修饰符用于让点号 . 匹配任何字符,包括换行符。
re.U 此修饰符用于将字符解释为 Unicode 字符集。
re.X 它用于忽略空白字符,并将 # 作为注释标记。

使用多个标志值

请看下面的 Python 代码,您将看到如何使用多个标志值来改变正则表达式的行为。多个标志值可以通过按位 OR (|) 运算符组合:

import re

s = re.search("L", "Hello")
print(s)  # Output: None, L is there but in small letter and we didn't use flags

s = re.search("L", "Hello", re.I)
print(s)  # Output: 1

s = re.search("L", "^Hello", re.I | re.M)
print(s)  # Output: 1, searching will be made from the start of line and case is ignored

检查允许的字符

您还可以检查某个字符串是否包含某个特定范围的字符。

定义函数并检查允许的字符

请看下面的例子,其中定义了一个函数,并使用预编译模式来检查传入的字符串中是否包含特定的字符:

import re


def check(str):
    s = re.compile(r"[^A-Z]")
    str = s.search(str)
    return not bool(str)


print(check("HELLOPYTHON"))  # Output: True
print(check("hellopython"))  # Output: False

在这个函数中,编译了模式 r'[^A-Z]',并在调用这个名为 check 的函数时,用它在传入的字符串中进行搜索。这个函数实际上检查传入的字符串是否包含字母 A-Z(大写字母)。同样可以看到,当您传入小写字母组成的字符串时,返回的是 false。

搜索和替换

re 模块提供了一个 sub 函数,用于使用函数中的 repl 属性替换给定 string 中 pattern 的所有出现。替换会一直进行到达到 count 指定的次数。sub 函数将返回更新后的字符串。

下面是 sub 函数的语法:

re.sub(pattern, repl, string, count=0)

使用 sub 函数

请看下面的例子,其中 sub 函数用给定的字符串替换了整个字符串:

import re

s = "Playing 4 hours a day"
obj = re.sub(r"^.*$", "Working", s)
print(obj)
Working

这里使用了 sub 函数。模式 r'^.*$ 表示从字符串的开头开始,然后 .* 表示字符串中的任意内容,直到字符串的末尾 $。然后参数 "Working" 将替换整个字符串 s。

使用 sub 函数删除字符串中的所有数字

请看下面的例子,其中 sub 函数删除了给定字符串中的数字。为此您可以使用 \d:

import re

s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r"\d", "", s)
print(obj)
Working   five   hours   a  day

同样,您也可以删除字符串中的字符。为此您可以使用 \D。

import re

s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r"\D", "", s)
print(obj)
76823437892346563244646345

findall() 函数

findall 函数返回与模式匹配的所有字符串组成的列表。search 和 findall 函数的区别在于,findall 会找到所有匹配,而 search 只找到第一个匹配。此函数会找到不重叠的匹配,并将它们作为字符串列表返回。

下面是 findall 函数的语法:

findall(pattern, string, flags)

这里 pattern 是正则表达式模式,您将在给定的 string 中查找它,并带上一些 flags 值,例如用 re.I 来忽略大小写。

查找所有不重叠的匹配

在下面的例子中,findall 会找到不重叠的匹配:

import re

str = "Working 6 hours a day. Studying 4 hours a day."
mobj = re.findall(r"[0-9]", str)
print(mobj)
["6", "4"]

r'[0-9]' 是一个在给定字符串中查找所有数字的模式,返回的是一个字符串列表(无论它们是否为数字),并存储在 mobj 中。

对文件使用 findall

您也可以使用 findall 在文件中查找。当您对文件使用 findall 时,它将返回文件中所有匹配字符串的列表。由于会使用文件的 read() 函数,您不必使用循环逐行遍历文件,因为它将文件的全部文本作为字符串返回。请看下面的例子:

import re

file = open("asd.txt", "r")
mobj = re.findall(r"arg.", file.read())
print(mobj)
file.close()
["arg,", "arg,", "arg,", "argv", "argv", "argv"]

在这个例子中,文件首先以读取模式打开。模式 r'arg.' 与文件的内容进行匹配,输出中就是匹配字符串的列表。

finditer() 函数

finditer 函数可用于在字符串中查找正则表达式模式,并同时给出匹配字符串的位置,即字符串的索引。此函数实际上会迭代匹配的字符串,并返回字符串的索引或位置。

下面是 finditer 函数的语法:

finditer(pattern, string, flags)

迭代匹配结果

findall 和 finditer 之间唯一的区别是,finditer 在给出匹配字符串的同时还返回索引。在下面的代码中,finditer 用于在使用 for 循环迭代匹配(匹配字符串)时找到匹配字符串的位置。

import re

str = "Working 6 hours a day. Studying 4 hours a day."
pat = r"[0-9]"
for mobj in re.finditer(pat, str):
    s = mobj.start()
    e = mobj.end()
    g = mobj.group()
    print("{} found at location [{},{}]".format(g, s, e))
6 found at location [8,9]
4 found at location [32,33]

在这个例子中,模式是要在 str 中查找的 0 到 9 的数字。for 循环迭代 finditer 返回的匹配字符串。在循环中,start、end 和 group 函数分别返回 finditer 返回的字符串在每次迭代中的起始索引、结束索引和找到的匹配。

split() 函数

split 函数用于分割字符串。

下面是 split 函数的语法:

split(patter, string, maxsplit, flags)

这里 max 是字符串分割的总次数。如果最多发生 maxsplit 次分割,字符串的剩余部分将作为列表的最后一个元素返回。max 的默认值是 0,表示不限制分割次数。

分割字符串

split 函数返回字符串中的每个单词

在下面的代码中,字符串按照给定的模式和最大分割次数进行分割。

import re

str = "Birds fly high in the sky for ever"
mobj = re.split("\s+", str, 5)
print(mobj)
["Birds", "fly", "high", "in", "the", "sky for ever"]

在这个例子中,模式字符 \s 是一个特殊字符,它匹配空白字符,等价于 [ \t\n\r\f\v]。因此您可以将单词分开。这里的 max 值是 5,这样会进行 6 次分割,最后一个元素是第 5 次分割后字符串的剩余部分。

re 的基本模式

正则表达式可以指定与给定字符串进行比较的模式。以下是正则表达式的基本模式:

模式 描述
^ 它用于匹配字符串的开头。
$ 此模式将匹配字符串的末尾。
. 点号用于匹配一个字符(不包括换行符)。
[...] 它用于匹配方括号内的单个字符。
[^...] 它将匹配单个字符,但该字符不在方括号内。
* 给定字符串中前面的正则表达式出现 0 次或多次。
+ 给定字符串中前面的正则表达式出现 1 次或多次。
? 给定字符串中前面的正则表达式出现 0 次或 1 次。
{n} 它将匹配给定字符串中出现的次数为 n 次。
{n,} 它将匹配 n 次或更多次出现。
{n,m} 此模式用于匹配字符串中至少 n 次、至多 m 次的匹配。
`a b`
(re) 此模式用于对正则表达式进行分组,并会记住匹配的文本。
(?imx) 它将临时开启 RE 中的 i 或 m 或 x。使用括号时,仅括号区域受影响。
(?-imx) 它将临时关闭 RE 中的 i 或 m 或 x。使用括号时,仅括号区域受影响。
(?: re) 此模式用于对正则表达式进行分组,但不会记住匹配的文本。
(?imx: re) 它将临时开启括号内 RE 中的 i 或 m 或 x。
(?-imx: re) 它将临时关闭括号内 RE 中的 i 或 m 或 x。
(?#...) 它是一个注释。
(?= re) 它用于使用模式指定位置。它没有任何范围。
(?! re) 它用于使用模式否定来指定位置。它没有任何范围。
(?> re) 此模式用于匹配独立模式。
\w 此模式用于匹配单词字符。
\W 此模式用于匹配非单词字符。
\s 它将匹配空白字符。\s 等价于 [ \t\n\r\f]。
\S 它将匹配非空白字符。
\d 等价于 [0-9]。它匹配字符串中的数字。
\D 它匹配非数字字符。
\A 匹配字符串的开头。
\Z 匹配字符串的末尾。如果有换行符,它将在换行符之前匹配。
\G 匹配上一次匹配结束的位置。
\b 在方括号外时匹配单词边界,在方括号内时匹配退格符。
\B 匹配非单词边界。
\n, \t, etc. \n 用于匹配换行符,\t 将匹配制表符,以此类推。
\1...\9 此模式将匹配第 n 个子表达式(分组)。
\10 如果已经完成匹配,\10 通常匹配第 n 个子表达式(分组)。如果尚未完成匹配,\10 将提供字符代码的八进制表示。

重复的情况

下表展示了一些重复情况的例子及描述:

例子 描述
ab? 它将匹配 a 或 ab。
ab* ab* 将匹配 ab、a 以及任意个 a 后跟任意个 b。
ab+ ab+ 表示 a 后跟 b,而不仅仅是 a。a 后面必须跟非零个数的 b。
\d{2} 它将恰好匹配 2 位数字。
\d{2,} 它将匹配 2 位或更多位数字。
\d{2,4} 它将匹配 2、3 和 4 位数字。

非贪婪重复

在正则表达式中,重复默认是贪婪的,它会尝试匹配尽可能多的重复。

*、+ 和 ? 等限定符都是贪婪限定符。当您使用 .* 时,它会执行贪婪匹配并匹配整个字符串,从而尽可能多地匹配字符。请看下面的代码:

import re

mobj = re.match(r".*", "Birds fly high in sky")
print(mobj.group())
Birds fly high in the sky

可以看到,这里整个字符串都被匹配了。

当您在 .+ 后加上 ? 时,就会得到一个非贪婪的正则表达式,模式 .+? 将在字符串中匹配尽可能少的字符。

import re

mobj = re.match(r".*", "Birds fly high in sky")
print(mobj.group())

结果是字符串的第一个字符

B

re 中的特殊字符和序列

re 中的特殊字符以 \ 开头。例如,我们有 \A,它将从字符串的开头匹配。

这些特殊字符已在上表中进行了描述。

在本节中,将为您演示其中一些特殊字符的例子:

import re

str = "Birds fly high in the sky"
# \A
# OUTPUT: B, here \A will match at beginning only.
mobj = re.match(r"\Ab", str, re.I)

# \d
mobj = re.match(r"\d", "4 birds are flying")  # OUTPUT: 4

# \s
mobj = re.split("\s+", "birds fly high in the sky", 1)  # OUTPUT: ['Birds', 'fly']

escape 函数

escape 函数用于转义字符串中的所有字符。ASCII 字母、数字和 _ 不会被转义。当您想从字符串中提取元字符时,可以使用 escape 函数。

下面是 escape 函数的语法:

escape(pattern)

在下面的例子中,字符串 www.python.org 被传递给 escape 函数。其中 . 是一个元字符,它将被提取或匹配:

print(re.escape("www.python.org"))
www\.python\.org

这里 . 是一个被提取或匹配的元字符。每当使用 escape 函数匹配元字符时,该字符前面都会有 \。

转义特殊字符

像方括号 [ 和 ] 这样的字符无法直接匹配。请看下面的例子:

import re

mobj = re.search(r"[a]", "[a]b")
print(mobj.group())
a

可以看到,方括号 [ 和 ] 没有被匹配。

您可以使用 escape 函数来匹配它们:

import re

mobj = re.search(r"\[a\]", "[a]b")
print(mobj.group())
[a]b

group() 函数

group 函数用于返回找到的匹配中的一个或多个子组。group 函数可以带有一些参数。

下面是 group 函数的语法:

group(group1, group2, ..., groupN)

如果 group 函数只有一个参数,结果将是一个字符串;但当参数多于一个时,结果将是一个元组(每个参数对应一项)。

当没有参数时,参数默认为零,它将返回整个匹配。

当参数 groupN 为零时,返回值将是整个匹配字符串。

当您将组编号或参数指定为负值,或大于模式中组的数量的值时,将发生 IndexError 异常。

请看下面的代码,其中 group 函数没有参数,这等价于 group(0)。

import re

str = "Working 6 hours a day"
mobj = re.match(r"^.*", str)
print(mobj.group())
Working 6 hours a day

这里使用了 group(),您得到的是整个匹配的字符串。

提取匹配文本的部分

在下面的例子中,group 函数与参数一起使用,以提取匹配的组:

import re

a = re.compile("(p(q)r)s")
b = a.match("pqrs")
print(b.group(0))
print(b.group(1))
print(b.group(2))
pqrs
pqr
q

这里 group(0) 返回整个匹配。group(1) 将返回第一个匹配,即 pqr,group(2) 将返回第二个匹配,即 q。

命名组

使用命名组,您可以创建一个捕获组。之后可以通过名称引用这个组。请看下面的例子:

import re

mobj = re.search(r"Hi (?P<name>\w+)", "Hi Roger")
print(mobj.group("name"))
Roger

非捕获组

非捕获组可以使用 ?: 创建。当您不想要组的内容时,可以使用非捕获组。

import re

mobj = re.match("(?:[pqr])+", "pqr")
print(mobj.groups())
()