Python 正则表达式指南:文本处理的瑞士军刀
在数据世界里,文本数据无所不在,处理它们是日常工作的一部分。无论是数据清洗、日志分析还是文本挖掘,都需要一种既强大又灵活的工具。而正则表达式,一种用精巧的符号序列构建的技术,就像是程序员的瑞士军刀,简约而不简单,赋予文本处理以惊人的力量。本博客将带你深入正则表达式的世界,展示它如何简化复杂的文本任务。
正则表达式并不是 Python 独有的工具,它同样适用于其他编程语言。然而,Python 通过 re 库提供了对正则表达式的完整支持。利用这个强大的库,我们可以在 Python 环境中灵活地运用正则表达式。在 Python 中,几乎所有正则表达式的操作都依赖于 re 库。下面,我们将列出一些常用的匹配规则,并探讨 re 库中的一些常用方法。
1、正则表达式模式
以下表格总结了一些基本的正则表达式模式及其描述:
| 模式 | 描述 |
|---|---|
\w | 匹配字母、数字及下划线 |
\W | 匹配非字母、数字及下划线的字符 |
\s | 匹配任意空白字符,等价于 [\t\n\r\f] |
\S | 匹配任意非空白字符 |
\d | 匹配任意数字,等价于 [0-9] |
\D | 匹配非数字字符 |
\A | 匹配字符串开头 |
\Z | 匹配字符串结尾(不包括换行符) |
\z | 匹配字符串结尾(包括换行符) |
\G | 匹配最后一次匹配结束的位置 |
\n | 匹配换行符 |
\t | 匹配制表符 |
^ | 匹配一行的开头 |
$ | 匹配一行的结尾 |
. | 匹配任意字符(默认不包括换行符),使用 re.DOTALL 标记后,可匹配包括换行符的任意字符 |
[...] | 匹配方括号内的任一字符,例如 [amk] 匹配 a、m 或 k |
[^...] | 匹配不在方括号内的任一字符,例如 [^abc] 匹配除 a、b、c 之外的任意字符 |
* | 匹配 0 次或多次前面的表达式 |
+ | 匹配 1 次或多次前面的表达式 |
? | 匹配 0 次或 1 次前面的表达式,采用非贪婪方式 |
{n} | 精确匹配 n 次前面的表达式 |
{n, m} | 匹配 n 到 m 次前面的表达式,采用贪婪方式 |
| `a | b` |
() | 将括号内的表达式作为一个分组 |
2、正则表达式修饰符(可选标志)
修饰符可以改变正则表达式的匹配行为。以下是一些常用的正则表达式修饰符及其作用:
| 修饰符 | 描述 |
|---|---|
re.I | 使匹配对大小写不敏感,即忽略大小写 |
re.L | 使 \w、\W、\b、\B、\s、\S 这些特殊字符集依赖于当前环境 |
re.M | 多行匹配,影响 ^ 和 $ 的行为 |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 使 \w、\W、\b、\B、\d、\D、\s、\S 依赖于 Unicode 字符属性数据库 |
re.X | 提高可读性,允许在正则表达式中添加空格和注释(# 后面的内容) |
3、正则表达式实例
① 字符匹配
这是最基础的匹配方式,直接通过字面值进行匹配。
| 实例 | 描述 |
|---|---|
python | 匹配字符串 “python” |
② 字符类
字符类允许你匹配指定的字符集合。通过使用方括号,可以指定一个字符集,从而匹配多种可能的字符。
| 实例 | 描述 |
|---|---|
[Pp]ython | 匹配 “Python” 或 “python” |
pytho[ne] | 匹配 “python” 或 “pythoe” |
[aeiou] | 匹配任一元音字母 |
[0-9] | 匹配任一数字,等同于 [0123456789] |
[a-z] | 匹配任一小写字母 |
[A-Z] | 匹配任一大写字母 |
[a-zA-Z0-9] | 匹配任一字母或数字 |
[^aeiou] | 匹配除 “aeiou” 以外的任意字符 |
[^0-9] | 匹配任意非数字字符 |
③ 特殊字符类
特殊字符类用于匹配特定类型的字符,如数字、空白等,而无需显式地列出所有可能的字符。
| 实例 | 描述 |
|---|---|
. | 匹配除 \n 之外的任何单个字符。要匹配包括 \n 在内的任何字符,请使用 [.\n] 模式。 |
\d | 匹配一个数字字符,等价于 [0-9]。 |
\D | 匹配一个非数字字符,等价于 [^0-9]。 |
\s | 匹配任何空白字符,等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符,等价于 [^ \f\n\r\t\v]。 |
\w | 匹配包括下划线的任何单词字符,等价于 [A-Za-z0-9_]。 |
\W | 匹配任何非单词字符,等价于 [^A-Za-z0-9_]。 |
4、正则表达式方法
re.match() 函数
re.match() 函数尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 None。
re.match(pattern, string, flags=0)参数说明:
pattern:要匹配的正则表达式。string:要匹配的字符串。flags:标志位,用于控制正则表达式的匹配方式。
匹配成功,re.match 方法返回一个匹配的对象;否则返回 None,我们可以使用 group(num) 或 groups() 方法从匹配对象中获取信息。
re.search() 函数
re.search() 函数扫描整个字符串并返回第一个成功的匹配。
re.search(pattern, string, flags=0)与 re.match 类似,re.search 在匹配成功时返回一个匹配对象,否则返回 None。
re.match() 与 re.search() 的区别
re.match() 只匹配字符串的开始部分,如果开头不符合正则表达式,则匹配失败,函数返回 None;而 re.search() 匹配整个字符串,直到找到一个匹配项。
import re
string = "This is Pluto's personal blog"
match = re.match(r'blog', string, re.M|re.I)
if match:
print("match.group() : ", match.group())
else:
print("No match!!")
search = re.search(r'blog', string, re.M|re.I)
if search:
print("search.group() : ", search.group())
else:
print("No search!!")输出结果将显示 “No match!!” 和 “search.group() : blog”,因为 match() 没有在字符串开头找到 “blog”,而 search() 在整个字符串中进行了搜索。
re.sub() 函数
re.sub() 函数用于替换字符串中的匹配项。
re.sub(pattern, repl, string, count=0, flags=0)参数说明:
pattern:匹配的正则表达式。repl:替换的字符串或一个函数。string:要匹配的字符串。count:模式匹配后替换的最大次数,默认为 0,意味着替换所有的匹配。
re.compile() 函数
re.compile() 函数用于编译正则表达式,生成一个正则表达式对象(Pattern 对象),供 match() 和 search() 等函数使用。
re.compile(pattern, flags=0)通过预编译可以提高正则表达式的重用性,并提升匹配效率。
findall() 和 finditer() 函数
findall() 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表;finditer() 返回一个迭代器,其中每个元素都是 MatchObject 实例。
re.split() 函数
re.split() 函数按照正则表达式匹配的子串将字符串分割后返回列表。
5、正则表达式对象
在 Python 的 re 模块中,正则表达式的编译和匹配操作都与特定的对象相关联。这些对象增强了正则表达式的功能,使得文本处理更加高效和灵活。下面是两个最核心的对象:
re.RegexObject 对象
re.RegexObject 是由 re.compile() 方法返回的对象,它代表了一个编译后的正则表达式。这个对象预先编译了正则表达式的模式,并可被多次用于匹配操作。使用 re.RegexObject 的好处是,当你需要多次使用同一个正则表达式时,你不必在每次匹配时重新编译它,这样可以大大提高效率。
例如,如果你正在构建一个文本编辑器,需要高亮显示所有的 Python 变量名,你可以编译一个匹配 Python 变量命名规则的正则表达式,并在每次文本更新时使用它。
import re
# 编译一个匹配 Python 变量名的正则表达式
variable_regex = re.compile(r'\b[a-zA-Z_][a-zA-Z_0-9]*\b')
# 在文本中多次使用
text = "Here are some variables: var1, var2, function_name, _private_var"
matches = variable_regex.findall(text)
for match in matches:
print("Found Python variable:", match)re.MatchObject 对象
re.MatchObject 是由 match()、search() 等匹配函数返回的对象,它包含了匹配的详细信息。这个对象提供了多种方法来获取关于匹配部分的信息,例如匹配的文本、匹配的位置等。
re.MatchObject 的常用方法包括:
group():返回匹配的字符串。start():返回匹配开始的索引。end():返回匹配结束的索引。span():返回一个包含匹配 (开始, 结束) 索引的元组。
下面是一个 re.MatchObject 的使用示例:
import re
# 匹配一个简单的日期格式:YYYY-MM-DD
date_pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2})')
text = "The event will take place on 2024-02-29."
match = date_pattern.search(text)
if match:
print("Matched date:", match.group()) # 获取整个匹配的部分
year, month, day = match.groups() # 获取各个组的匹配部分
print("Year:", year, "Month:", month, "Day:", day)
print("Match starts at index:", match.start())
print("Match ends at index:", match.end())
print("Match span:", match.span())通过使用这些对象和它们的方法,你可以更加精确地控制文本匹配和处理的过程,从而实现复杂的文本分析和数据提取任务。
6、常用表达式集锦
注意:正则表达式用于校验的场景非常多,而且有些表达式可能因为业务需求的不同而有所变化。上述表达式是基于常用的模式简化和优化后的结果,但在实际使用中可能需要根据具体情况进行调整。
校验数字的表达式
- 数字:
^\d+$ - n 位的数字:
^\d{n}$ - 至少 n 位的数字:
^\d{n,}$ - m-n 位的数字:
^\d{m,n}$ - 非零开头的数字:
^[1-9]\d*$ - 正整数:
^[1-9]\d*$ - 负整数:
^-[1-9]\d*$ - 非负整数(正整数 + 0):
^\d+$ - 非正整数(负整数 + 0):
^-\d+$ - 正浮点数:
^[1-9]\d*\.\d+|0\.\d*[1-9]\d*$ - 负浮点数:
^-([1-9]\d*\.\d+|0\.\d*[1-9]\d*)$ - 浮点数:
^-?([1-9]\d*\.\d+|0\.\d*[1-9]\d*|0?\.0+|0)$
校验字符的表达式
- 汉字:
^[\u4e00-\u9fa5]+$ - 英文和数字:
^[A-Za-z0-9]+$ - 长度为 n 的所有字符:
^.{n}$ - 由 26 个英文字母组成的字符串:
^[A-Za-z]+$ - 由 26 个大写英文字母组成的字符串:
^[A-Z]+$ - 由 26 个小写英文字母组成的字符串:
^[a-z]+$ - 由数字和 26 个英文字母组成的字符串:
^[A-Za-z0-9]+$ - 由数字、26 个英文字母或者下划线组成的字符串:
^\w+$
特殊需求表达式
- Email 地址:
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ - 域名:
^(?i:[a-z0-9]-?[a-z0-9]*\.)+[a-z]{2,}$ - Internet URL:
^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$ - 手机号码(简化匹配):
^\+?\d{10,13}$ - 身份证号(简化匹配):
^\d{15}(\d{2}[0-9xX])?$ - 帐号是否合法(字母开头,允许 5-16 字节,允许字母数字下划线):
^[a-zA-Z][a-zA-Z0-9_]{4,15}$ - 密码(以字母开头,长度在 6~18 之间,只能包含字母、数字和下划线):
^[a-zA-Z]\w{5,17}$ - 强密码(包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ - 日期格式(YYYY-MM-DD):
^\d{4}-\d{1,2}-\d{1,2}$ - 中国邮政编码:
^[1-9]\d{5}$ - IP 地址:
^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$
