正则表达式
正则表达式(Regular Expression,简称正则或RegExp)是一种强大的文本模式匹配工具,被广泛应用于字符串的搜索、替换、验证等场景。Python 的 re
库为正则表达式提供了丰富的支持,使得开发者能够在处理文本数据时更加高效和灵活。本文将深入探讨 Python 中的 re
库,包括基本语法、常见用法、高级技巧以及一些最佳实践,旨在帮助读者更全面地理解和运用正则表达式。
什么是正则表达式?
正则表达式是一种用于描述字符串匹配规则的表达式。它由普通字符和元字符(特殊字符)组成,通过这些字符的组合,可以构建出具有强大匹配能力的规则。正则表达式在处理文本时能够实现高度灵活的模式匹配,从而满足不同场景下的需求。
1.10.1 正则表达式的基本用法
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。Python
自1.5
版本起增加了re
模块,它提供 Perl
风格的正则表达式模式。re
模块使 Python
语言拥有全部的正则表达式功能。compile
函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。re
模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。本章节主要介绍Python
中常用的正则表达式处理函数。
基本正则表达式模式
匹配字符 | 作用 |
---|---|
. | 匹配任意字符(除了换行符 \n )。 |
\d | 匹配任意数字,相当于 [0-9] 。 |
\D | 匹配任意非数字字符,相当于 [^0-9] 。 |
\w | 匹配任意字母、数字或下划线,相当于 [a-zA-Z0-9_] 。 |
\W | 匹配任意非字母、数字或下划线字符,相当于 [^a-zA-Z0-9_] 。 |
\s | 匹配任意空白字符,包括空格、制表符、换行符等。 |
\S | 匹配任意非空白字符。 |
* | 匹配前面的字符零次或多次。 |
+ | 匹配前面的字符至少一次。 |
? | 匹配前面的字符零次或一次。 |
{n} | 匹配前面的字符恰好 n 次。 |
{n,m} | 匹配前面的字符至少 n 次,最多 m 次。 |
^ | 匹配字符串的开头。 |
$ | 匹配字符串的结尾。 |
\b | 匹配单词的边界。 |
使用正则表达式进行匹配
可以使用编译后的正则表达式对象进行匹配操作。以下是一些常见的匹配方法:
方法 | 作用 |
---|---|
match() | 从字符串的开头开始匹配。 |
search() | 在字符串中搜索匹配项。 |
findall() | 查找字符串中所有匹配项。 |
finditer() | 返回一个迭代器,用于迭代查找到的匹配项。 |
Python
中使用正则表达式时,通常会使用内置的re
模块。使用re.compile
函数将正则表达模式编译成一个可重复使用的对象。
语法结构:
import re
pattern = re.compile(r'pat')
参数说明:pat
指的是正则表达式的匹配模式,例如\d+
匹配任意数字。
re.match
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()
就返回 none
。
语法结构:
re.match(pattern, string, flags=0)
参数说明:pattern
指的是匹配的正则表达式,string
指的是要匹配的字符串,flag
指的是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。匹配成功 re.match
方法返回一个匹配的对象,否则返回 None
。我们可以使用 group(num)
或 groups()
匹配对象函数来获取匹配表达式。
示例:匹配从字符串的开头开始的一个或多个数字
实现原理:
- 导入
re
模块,这是一个正则表达式的模块,用于在Python中进行字符串匹配。 - 定义一个名为
match_example
的函数,该函数不会接收任何参数,但在函数内部使用re.compile
函数编译一个正则表达式模式。这个模式是用来匹配一个或多个数字字符。 - 使用
re.match
函数在给定的字符串中查找与编译的正则表达式模式匹配的子字符串。如果找到了匹配项,re.match
将返回一个匹配对象,否则将返回None
。 - 在
if
语句中,检查result
是否为None
。如果result
不是None
,这意味着找到了匹配项,打印匹配项。否则,打印一条消息表示没有找到匹配项。
import re
def match_example():
pattern = re.compile(r'\d+')
result = pattern.match('123abc456')
if result:
print("match_example:", result.group())
else:
print("No match")
if __name__ == "__main__":
match_example()
输出结果:
match_example: 123
re.search
扫描整个字符串并返回第一个成功的匹配。
语法结构:
re.search(pattern, string, flags=0)
参数说明:pattern
指的是匹配的正则表达式,string
指的是要匹配的字符串,flag
指的是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。匹配成功re.search
方法返回一个匹配的对象,否则返回None
。我们可以使用group(num)
或 groups()
匹配对象函数来获取匹配表达式。
示例:在字符串中搜索并匹配一个或多个数字
实现原理:
- 定义
search_example
函数:这个函数使用re.compile()
方法来编译一个正则表达式模式,该模式匹配一个或多个数字。 - 使用
pattern.search()
方法在给定的字符串中查找匹配项:这个方法会返回一个匹配对象,如果找到匹配项,则返回None
。 - 根据匹配结果执行相应的操作:如果找到匹配项,打印匹配项的内容;否则,打印
"No match"
。
注意事项:
- 正则表达式模式中的特殊字符需要用反斜杠(
\
)转义,以表示它们原来的意义。例如,匹配单个数字,我们需要使用正则表达式\d+
,而不是\d
。 - 正则表达式中的
+
符号表示匹配一个或多个字符,\d
表示匹配一个数字字符。 - 在实际应用中,建议使用
re.finditer()
方法来查找所有匹配项,而不是re.search()
方法。这样可以方便地获取所有匹配项的列表,并执行进一步的操作。
import re
def search_example():
pattern = re.compile(r'\d+')
result = pattern.search('abc123def456')
if result:
print("search_example:", result.group())
else:
print("No match")
if __name__ == "__main__":
search_example()
输出结果:
search_example: 123
re.match
与re.search
的区别:re.match
只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None
;而re.search
匹配整个字符串,直到找到一个匹配。
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。match
和 search
是匹配一次,而 findall
匹配所有。
语法结构:
re.findall(string[, pos[, endpos]])
参数说明:string
指的是待匹配的字符串,pos
是可选参数,指定字符串的起始位置,默认为 0。endpos
是可选参数,指定字符串的结束位置,默认为字符串的长度。
示例:查找字符串中所有匹配的一个或多个数字
实现原理:
- 定义一个名为
findall_example
的函数,该函数包含两句话说说腾讯怎么研发你的。 - 在
findall_example
函数中,使用re.compile()
函数创建一个正则表达式对象,参数r'\d+'
表示匹配一个或多个数字字符。 - 使用
findall()
方法在给定的字符串'abc123def456'
中查找所有匹配pattern
的子字符串,并将结果存储在变量result
中。
注意:
- 正则表达式通常使用字符串的
re
模块来处理,例如re.compile()
用于创建正则表达式对象,re.findall()
用于查找所有匹配的子字符串。 - 正则表达式通常包含特殊字符,如
\d
表示匹配一个数字字符,+
表示匹配一个或多个字符。 - 在实际应用中,为了提高匹配的准确性,需要对正则表达式进行优化,例如添加
re.IGNORECASE
标志以忽略大小写,或者使用re.DOTALL
标志以匹配包括换行符在内的所有字符。
import re
def findall_example():
pattern = re.compile(r'\d+')
result = pattern.findall('abc123def456')
print("findall_example:", result)
if __name__ == "__main__":
findall_example()
输出结果:
findall_example: ['123', '456']
finditer
和 findall
类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
语法结构:
re.finditer(pattern, string, flags=0)
参数说明:pattern
指的是匹配的正则表达式,string
指的是要匹配的字符串,flag
指的是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
示例:使用迭代器查找字符串中的所有匹配项。
实现原理:找出输入字符串中所有匹配正则表达式\d+
(即匹配一个或多个数字)的所有匹配项。具体实现原理是,使用re
库的compile()
函数将正则表达式编译成一个Pattern
对象。然后,使用pattern.finditer()
函数在输入字符串中查找所有匹配项,并返回一个迭代器。最后,使用for
循环遍历这个迭代器,打印出每个匹配项。
代码中需要注意以下几点:
- 使用
re.compile()
函数将正则表达式编译成Pattern
对象,这样可以提高查找效率。 - 使用
pattern.finditer()
函数在输入字符串中查找所有匹配项,并返回一个迭代器。 - 使用
for
循环遍历这个迭代器,打印出每个匹配项。
import re
def finditer_example():
pattern = re.compile(r'\d+')
result_iterator = pattern.finditer('abc123def456')
for result in result_iterator:
print("finditer_example:", result.group())
if __name__ == "__main__":
finditer_example()
输出结果:
finditer_example: 123
finditer_example: 456
默认情况下,词量是贪婪的,匹配尽可能多的字符。在量词后加上?
可以实现非贪婪匹配
示例:分别使用贪婪匹配和非贪婪匹配,匹配一段相同的字符串,对比其作用效果。
实现原理:
- 贪婪匹配: 在正则表达式中,
<.*>
是一个通配符,表示匹配任意数量的字符,包括空字符。.*
表示匹配任意数量的字符,*
表示重复操作,因此<.*>
表示匹配任意数量的字符,直到遇到下一个>
字符。re.match()
函数会在字符串中查找与正则表达式匹配的子字符串,并返回一个匹配对象。 - 非贪婪匹配: 在正则表达式中,
<.*?>
是一个非贪婪通配符,表示匹配任意数量的字符,直到遇到下一个>
字符,但不会尽可能多地匹配字符。.*?
表示匹配任意数量的字符,*?
表示重复操作,但不会尽可能多地匹配字符。因此<.*?>
表示匹配任意数量的字符,直到遇到下一个>
字符,但不会尽可能多地匹配字符。re.match()
函数会在字符串中查找与正则表达式匹配的子字符串,并返回一个匹配对象。 - Python中的正则表达式模块
re
使用了libregex
库,一种高效的字符串匹配库。当我们在代码中使用正则表达式时,re
模块会自动调用libregex
库进行实际的匹配操作。
用途: 正则表达式通常用于文本处理任务,例如在HTML
文档中提取标签内容、在电子邮件中匹配邮件地址、在密码Crack
中识别复杂模式等。正则表达式可以有效地处理大量文本,并且可以根据需要调整匹配规则。
import re
# 贪婪匹配
def greedy_match():
pattern = re.compile(r'<.*>')
text = '<div> <ul> <li> Python Re </li> </ul> </div>'
result = pattern.match(text)
print(result.group())
# 非贪婪匹配
def no_greedy_match():
pattern = re.compile(r'<.*?>')
text = '<div> <ul> <li> Python Re </li> </ul> </div>'
result = pattern.match(text)
print(result.group())
if __name__ == '__main__':
greedy_match()
no_greedy_match()
输出结果:
<div> <ul> <li> Python Re </li> </ul> </div>
<div>
匹配 判断一个字符串是否满足正则表达式规则;切割 把字符串中符合正则规则的部分进行切割;替换把字符串中符合正则规则的部分 替换成新的部分;获取 把字符串中符合正则规则的部分单独获取出来。
方法 | 作用 |
---|---|
re.split() | split 方法按照能够匹配的子串将字符串分割后返回列表 |
re.sub | 替换字符串中的匹配项 |
语法结构:
re.split(pattern, string[, maxsplit=0, flags=0])
参数说明:pattern
指的是匹配的正则表达式,string
指的是要匹配的字符串,flag
指的是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。maxsplit
指的是分割次数,maxsplit=1
分割一次,默认为 0,不限制次数。
re.sub(pattern, repl, string, count=0, flags=0)
参数说明:pattern
指的是正则中的模式字符串,repl
指的是替换的字符串,也可为一个函数。string
指的是要被查找替换的原始字符串。count
指的是 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。flags
指的是编译时用的匹配模式,数字形式。
示例:替换字符串中的匹配项,分割字符串。
实现原理:
re_sub()
函数: 实现原理:使用re.sub()
函数来替换字符串中的某个子串。 用途:在输出中将其替换为Python
,而不是world
。 注意事项:re.sub()
函数的第一个参数是待替换的子串,第二个参数是替换后的子串,第三个参数是待处理的字符串。re_split()
函数: 实现原理:使用re.split()
函数来根据指定的分隔符(正则表达式)来分割字符串。 用途:在输出中将其分割。 注意事项:re.split()
函数的参数是分隔符的正则表达式,而不是单个字符。
以下是对代码的逐行解释:
- 定义一个名为re_sub的函数。在re_sub函数中,使用re.sub()函数将字符串"Hello world"中的"world"子串替换为"Python"。
- 定义一个名为re_split的函数。在re_split函数中,使用re.split()函数根据空格(正则表达式表示为\s)来分割字符串"Hello Big Data",并将结果存储在列表中。
import re
def re_sub():
new_text = re.sub('world', 'Python', 'Hello world')
print(new_text)
def re_split():
result = re.split(r'\s', 'Hello Big Data')
print(result)
if __name__ == '__main__':
re_sub()
re_split()
输出结果:
Hello Python
['Hello', 'Big', 'Data']
1.10.2 案例分析
Python 的正则表达式(通过 re
模块)在文本处理中非常有用,可以应用于多种场景,包括但不限于:
- 数据验证:验证输入是否符合特定的格式,例如电子邮件地址、电话号码、邮政编码等。
- 数据抽取:从大量文本中提取特定信息,例如从网页中抽取链接、从日志文件中抽取错误信息等。
- 数据清洗:移除或替换文本中的无关信息,例如删除多余的空格、换行符或特殊字符。
- 文本分割:根据特定的模式将文本分割成多个部分,例如按段落、句子或单词分割。
- 搜索和替换:在文本中查找特定的模式,并进行替换,例如批量替换文本中的特定词汇。
- 自然语言处理(NLP):用于分词、词性标注、命名实体识别等任务,正则表达式可以用来识别文本中的特定语言结构。
- 日志分析:分析日志文件,提取关键信息,如用户行为、错误报告、系统状态等。
- 配置文件解析:解析配置文件,提取配置项的值。
- 网络爬虫:在网页爬取中,正则表达式可以用来解析 HTML/XML 文档,提取有用的数据。
- 密码安全性检查:检查密码是否符合复杂性要求,例如包含数字、大写字母、小写字母和特殊字符。
- 文件名处理:用来批量重命名文件,或从文件名中提取信息。
- 代码分析和重构:在代码库中查找特定的代码模式,进行代码重构或代码迁移。
正则表达式在文本处理中的强大之处在于它的灵活性和表达能力。它可以处理非常复杂的文本匹配和操作,同时保持代码的简洁性。然而,正则表达式也可能因为其复杂性而难以阅读和维护,因此在使用时需要权衡其带来的便利和潜在的理解难度。
示例:对一段文本信息进行数据提取,获取手机号码信息、邮箱地址信息、网站地址信息。
实现原理:
re.compile
:将正则表达式编译为正则表达式对象,可以提高匹配效率。r'1[3456789]\d{9}'
:1
:匹配数字1,在这里指的是匹配以1
开头的数字字符串。[23456789]
:匹配2、3、4、5、6、7、8或9中的一个数字。\d{9}
:匹配9个数字字符。
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
:\b
:这是一个单词边界,用于匹配单词的边界。[A-Za-z0-9._%+-]+
:这是一个字符序列,匹配任意数量的大写字母、小写字母、数字、点号、百分号、加号或减号。@
:匹配字符@
。[A-Z|a-z]{2,}
:这是一个字符序列,匹配两个或更多的大写字母或小写字母。\.[A-Z|a-z]{2,}
:这是一个字符序列,匹配一个点号,后面跟着两个或更多的大写字母或小写字母。\b
:这是一个单词边界,用于匹配单词的边界。
r'https?://\S+|www\.\S+'
:https?
表示匹配HTTP或HTTPS协议的URL,://
表示协议部分,\S+
表示匹配任意数量的非空白字符(包括除/之外的所有字符),|
表示或者,www.
表示匹配带有www前缀的URL,\S+
表示匹配任意数量的非空白字符。
从一段文字中提取出手机号码、邮箱地址和网址。使用了正则表达式(re)库来编写三个正则表达式模式,分别用于匹配手机号码、邮箱地址和网址。然后,使用
findall`方法从文本中查找匹配项,并将结果打印出来。
import re
text = """
这是一段文字,包含手机号码:13812345678和15098765432,还有邮箱地址:test@example.com,以及网址:https://www.baidu.com。
"""
phone_pattern = re.compile(r'1[3456789]\d{9}')
email_pattern = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
url_pattern = re.compile(r'https?://\S+|www\.\S+')
phone_numbers = phone_pattern.findall(text)
emails = email_pattern.findall(text)
urls = url_pattern.findall(text)
print("手机号码:", phone_numbers)
print("邮箱地址:", emails)
print("网址:", urls)
输出结果:
手机号码: ['13812345678', '15098765432']
邮箱地址: ['test@example.com']
网址: ['https://www.baidu.com。']