【全面指南】正则表达式:文本处理的瑞士军刀
在数据世界里,文本数据无所不在,处理它们是日常工作的一部分。数据清洗、日志分析或是文本挖掘,都需要一种既强大又灵活的工具。而正则表达式,一种用精巧的符号序列构建的技术,就像是程序员的瑞士军刀,简约而不简单,赋予文本处理以惊人的力量。本博客将带你深入正则表达式的世界,展示它如何简化复杂的文本任务。
正则表达式并不是 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 | 匹配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、常用表达式集锦
[scode type=“blue” size=“simple”]注意,正则表达式用于校验的场景非常多,而且有些表达式可能因为业务需求的不同而有所变化。上述表达式是基于常用的模式简化和优化后的结果,但在实际使用中可能需要根据具体情况进行调整。[/scode]
校验数字的表达式
- 数字:
^\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)$