第16章 正则表达式
16.1 什么是正则表达式
正则表达式(regular expression,常简写为 regex、regexp 或 re),是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。正则表达式可以在文本中查找、替换、提取和验证特定的模式。
16.2 re 模块
Python 的 re 模块提供了正则表达式匹配操作。
import re
re 模块中提供了一些方法用于查找或处理字符串。
16.2.1 search
re.search(pattern, string)
扫描整个 string 查找正则表达式 pattern 产生匹配的第一个位置,并返回相应的 Match。如果字符串中没有与模式匹配的位置则返回 None。
16.2.2 match
re.match(pattern, string)
如果 string 开头的零个或多个字符与正则表达式 pattern 匹配,则返回相应的 Match。如果字符串与模式不匹配则返回 None。
16.2.3 findall
re.findall(pattern, string)
返回 pattern 在 string 中的所有非重叠匹配,以字符串列表或字符串元组列表的形式。对 string 的扫描从左至右,匹配结果按照找到的顺序返回。空匹配也包括在结果中。
16.2.4 sub
re.sub(pattern, repl, string, count=0)
返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。如果样式没有找到,则不加改变地返回 string。
repl可以是字符串或函数。- 如果
repl是一个函数,则它会针对每次pattern的非重叠出现的情况被调用。该函数接受单个Match参数,并返回替换字符串。 - 可选参数
count是要替换的最大次数;如果省略或设为 0,所有的匹配都会被替换。
16.2.5 split
re.split(pattern, string, maxsplit=0)
用 pattern 分开 string。如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零,最多进行 maxsplit 次分隔,剩下的字符全部返回到列表的最后一个元素。
16.3 表示字符
| 字符 | 描述 | ||
|---|---|---|---|
. | 匹配除 \r、\n 之外的任何单个字符。要匹配包括 \r、\n 在内的任何字符,请使用像 `(. | \r | \n)` 的模式。 |
\d | 匹配一个数字字符。等价于 [0-9]。 | ||
\D | 匹配一个非数字字符。等价于 [^0-9]。 | ||
\w | 匹配包括下划线的任何单词字符。等价于 [A-Za-z0-9_],注意 Unicode 正则表达式会匹配中文字符。 | ||
\W | 匹配任何非单词字符。等价于 [^A-Za-z0-9_]。 | ||
\s | 匹配任何空白字符,包括空格、制表符、换页符等。等价于 [ \f\n\r\t\v]。 | ||
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 | ||
[xyz] | 匹配所包含的任意一个字符。如 [abc] 可以匹配 "plain" 中的 "a"。 | ||
[^xyz] | 匹配未列出的任意字符。 | ||
[a-z] | 字符范围。匹配在 Unicode 编码表指定范围内的任意字符。 |
16.4 表示数量
| 字符 | 描述 |
|---|---|
* | 匹配前面的子表达式零次或多次。等价于 {0,}。 |
+ | 匹配前面的子表达式一次或多次。等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。 |
{n,} | 至少匹配 n 次。 |
{n,m} | 其中 n<=m。最少匹配 n 次且最多匹配 m 次。 |
?(非贪心) | 当该字符紧跟在任何一个其他重复修饰符后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串。例如对于字符串 "oooo",o+? 将匹配单个 "o",而 o+ 将匹配所有 "o"。 |
16.5 表示边界
| 字符 | 描述 |
|---|---|
^ | 匹配字符串的开始位置。 |
$ | 匹配字符串的结束位置。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。 |
\B | 匹配非单词边界。 |
16.6 匹配分组
| 字符 | 描述 |
|---|---|
x|y | 匹配 x 或 y |
(pattern) | 匹配 pattern 并获取这一匹配的子字符串。 |
(?P<name> pattern) | 与常规的圆括号类似,但分组所匹配到了子字符串可通过符号分组名称 name 来访问。 |
(?P=name) | 引用一个命名组合。 |
\num | 向后引用一个子字符串,该子字符串与正则表达式的第 num 个用括号围起来的子表达式匹配。例如:(.)\1 匹配两个连续的相同字符。 |
注意:
. ^ $ * + ? { } [ ] \ | ( )属于元字符。这两个元字符[和]用于指定一个字符类。元字符(除了\)在字符类中是不起作用的。例如,[akm$]将会匹配 'a', 'k', 'm' 或 '$'。
16.7 原始字符串
Python 中字符串前面加上 r 表示原始字符串,忽略转义。原始字符串非常适合用于正则表达式,因为正则表达式中通常包含很多反斜杠(例如 \d 或 \w),使用原始字符串可以避免反斜杠带来的转义问题。
import re
text = "abcdef123456"
print(re.search(r"\w+", text))
print(re.search("\w+", text)) # SyntaxWarning: invalid escape sequence '\w'
不使用原始字符串虽然也能运行,但是会有语法警告。
16.8 案例
16.8.1 匹配电话号码
import re
test = [
"13812345678", # 合法
"11456817239", # 非法
"19912345678", # 合法
"17138412356", # 合法
"1234567890", # 非法
"14752345673", # 合法
"1800123456", # 非法
]
# 以1开头,第二位为3,4,5,7,8,9,后面是9位数字
pattern = r"^1[345789]\d{9}$"
for i in test:
print(f"{i:20}{'合法' if re.match(pattern, i) else '非法'}")
16.8.2 匹配邮箱
import re
test = [
"example@example.com",
"user.name@subdomain.example.co",
"username@.com",
"@missingusername.com",
"-dasd@qq.com",
]
# 匹配邮箱
pattern = r"[\w!#$%&'*+-/=?^`{|}~.]+@[\w!#$%&'*+-/=?^`{|}~.]+\.[a-zA-Z]{2,}$"
for i in test:
print(f"{i:40}{'合法' if re.match(pattern, i) else '非法'}")
16.8.3 匹配 0-255 之间的数字
import re
test = ["0", "9", "50", "100", "199", "200", "255", "256", "-1", "01", "001"]
# 十位为1-9,?表示可以没有十位,个位是0-9
# 或 百位是1,十位是0-9,个位是0-9
# 或 百位是2,十位是0-4,个位是0-9
# 或 百位是2,十位是5,个位是0-5
pattern = r"^([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])$"
for num in test:
print(f"{num:5} {'合法' if re.match(pattern, num) else '非法'}")
16.8.4 从标签中获取网址
import re
test = """<link rel="alternate" hreflang="zh" href="https://zh.wikipedia.org/wiki/正则表达式">
<link rel="alternate" hreflang="zh-Hans" href="https://zh.wikipedia.org/zh-hans/正则表达式">
<link rel="alternate" hreflang="zh-Hant" href="https://zh.wikipedia.org/zh-hant/正则表达式">"""
# 获取所有href中网址
pattern = r'href="(.+?)"'
for i in re.findall(pattern, test):
print(i)
16.8.5 替换文本中的所有数字为对应的词
import re
test = "I have 2 apples and 3 oranges."
# 定义数字到词的映射
num_map = {"1": "one", "2": "two", "3": "three", "4": "four", "5": "five"}
print(re.sub(r"\d", lambda x: num_map[x.group(0)], test))
# I have two apples and three oranges.