1、简介
正则表达式(Regular expressions 也称为 REs,或 regexes 或 regex patterns)本质上是一个微小的且高度专业化的编程语言。这个概念最初是由 Unix 中的工具软件(例如 sed 和grep)普及开的。
正则表达式是对 字符串操作 的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。给定一个正则表达式和另一个字符串,可以通过正则表达式从字符串中获取我们想要的特定部分。正则表达式灵活性、逻辑性和功能性非常强,可以迅速地用极简单的方式达到字符串额复杂控制,但对于共接触的人来说比较晦涩难懂。由于 正则表达式主要应用对象是文本,因此他在各种文本编辑器场合都有应用。
Python 的正则表达式引擎是用 C 语言写的,所以效率是极高的。可以帮我们处理 98.3% 的文本任务。
1.1 处理字符串主要有四大功能
匹配 查看一个字符串是否符合正则表达式的语法,一般返回 true 或者 false
获取 正则表达式来提取字符串中符合要求的文本
替换 查找字符串中符合正则表达式的文本,并用相应的字符串替换
分割 使用正则表达式对字符串进行分割。
2、字符匹配与获取
2.1 开始使用
python 中,使用re
库来调用正则匹配
import re
2.2 常用匹配函数
函数 | 解释 |
---|---|
re.match(pattern, string, flags) | 返回 string 中所有与 pattern 相匹配的 第一个 字符串的 Match 对象 如果字符串没有匹配,则返回 None。 只从字符串的开始匹配字符。要求 pattern 位于 string 的开头 |
re.search(pattern, string, flags) | 返回 string 中所有与 pattern 相匹配的 第一个 字符串的 Match 对象 如果字符串没有匹配,则返回 None。 匹配整个字符串,直到找到一个匹配的对象 |
re.findall(pattern, string, flags) | 返回 string 中所有与 pattern 相匹配的 全部字符串 对象 返回形式为数组 |
re.finditer(pattern, string, flags) | 返回 string 中所有与 pattern 相匹配的 全部字串 对象 返回形式为迭代器。 |
re.compile(pattern, flags) | 函数根据一个模式字符串和可选的标志参数 返回一个正则表达式对象 |
pattern:匹配的正则表达式模板
string:输入要匹配的字符串
flags:标志位,用于控制正则表达式的匹配方式
2.2.1 Match object 方法
match() 和 search() 方法一旦匹配成功,就是一个 match object 对象,而 match object 对象有以下方法:
- group() 返回被 RE 匹配的字符串,默认 group(0)
- start() 返回匹配开始的位置
- end() 返回匹配结束的位置
- span()返回一个元组包含匹配 (开始, 结束) 的位置
2.2.2 compile
在直接使用字符串表示的正则表达式进行 search, match 和 findall 操作时,python 会将字符串转换为正则表达式对象。而使用 compile 完成一次转换之后,在每次使用模式的时候就不用重复转换。
当需要大量匹配时,为了节省匹配时间,可以使用 re.compile 先预处理。
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+')
# 使用 Pattern 对象的 findall 方法在字符串中查找匹配的内容
result = pattern.findall('hello 123 world 456')
print(result) #['123', '456']
编译后的 Pattern 对象提供了一系列方法用于匹配和查找文本,包括:
match(string): 从字符串的起始位置匹配正则表达式。
search(string): 扫描整个字符串并返回第一个成功的匹配。
findall(string): 返回字符串中所有非重叠匹配的列表。
finditer(string): 返回一个迭代器,产生字符串中所有非重叠匹配的 Match 对象。
split(): 根据匹配的子串将字符串分割。
sub(): 替换匹配的子串。
subn(): 替换匹配的子串并返回替换次数。
后面三个在第 3 章有介绍。
2.3 字符匹配模式
正则表达式 pattern 有两种匹配模式:
直接匹配:给啥匹配啥,完全匹配,不适用替代符(元字符)
元字符匹配:利用内置已定义的一类特殊字符,表达特定含义,进行匹配搜索。
2.3.1 直接匹配
import re
r=re.findall('Mi','Mike is Mifan,is him? ')
print(r)
# 输出结果如下
['Mi', 'Mi']
rs=re.search('Mi','Mike is Mifan,is him? ')
print(rs) #输出:<re.Match object; span=(0, 2), match='Mi'>
print(rs.group()) # 输出:Mi
2.1.2 元字符样式
首先一定要记住以下元字符及其代表的含义:

上述元字符中,有的字符 一个符号可以匹配多次,但是我们很多时候需要它匹配到一定时候停下来,这个时候就涉及两个概念:
贪婪模式:正则表达式一般趋向于最大长度匹配,默认形式。
非贪婪模式:在满足匹配时,匹配尽可能短的字符串。匹配到一块字符(可以为空),再去匹配下一处。
举例说明:
import re
str = "Total CPU time == 62361263, Real Time = 64321"
match_res1 = re.search(r"Real Time.*(\d+)", str) #默认模式, 贪婪模式
match_res2 = re.search(r"Real Time.*?(\d+)", str) #非贪婪模式,加?
if match_res1:
time = match_res1.group(1)
print(time) #贪婪模式输出:1
if match_res1:
time = match_res2.group(1)
print(time) #贪婪模式输出:64321
贪婪模式下, .*
会一致匹配任何字符直到最后,最后留了末尾 1 给到\d+
,小括号的作用是界定返回值的范围
2.4 特殊字符扩展
还有反斜杠带来的扩展:

2.5 标志位
还有标志位 flags,用于控制正则表达式的匹配方式:

最常用的可能是用标志位来控制 忽略大小写:
import re
str = "Total CPU time == 62361263, Real Time = 64321"
match_res = re.search(r"real time.*?(\d+)", str, re.IGNORECASE)
if match_res:
time = match_str.group(1)
print(time) #输出:64321
re.IGNORECASE 表示忽略匹配模板了忽略了大小写,如上所示,我们仍能匹配返回到 64321
2.6 举例
import re
str='Mike is Mifan,his age is 180 years old,and his weight is about 140 pound.'
rs = re.search('is about (.*[.]$)',str)
print(rs)
print(rs.group())
print(rs.group(1))
// 输出结果如下
<re.Match object; span=(54, 73), match='is about 140 pound.'>
is about 140 pound.
140 pound.
r=re.findall('his age is (\d+)',str)
print(r)
// 输出结果如下
['180']
r=re.findall('his age is \d+',str) // 与上对比,可说明 () 的作用
print(r)
// 输出结果如下
['his age is 180']
r=re.findall('his age is (\d+?)',str) // 问号前的 \d+ 由于问号,整体变成非贪婪模式
print(r)
// 输出结果如下
['1']
r=re.findall('his age is (.+)',str) //. 会匹配任何值,再加上 +,则会贪婪匹配后面所有内容
print(r)
// 输出结果如下
['180 years old,and his weight is about 140 pound.']
r=re.findall('\d+',str)
print(r)
// 输出结果如下
['180', '140']
r=re.findall('\d',str)
print(r)
// 输出结果如下
['1', '8', '0', '1', '4', '0']
r=re.findall('is about (.*[.]$)',str) //[]里填其他字符无效,会取出空值
print(r)
// 输出结果如下
['140 pound.']
r=re.findall('his (.*)',str)
print(r)
rs=re.findall('his (.*) years',str) // 这里的 '[空格]years' 有拦截效果
print(rs)
ra=re.findall('his (.*?) years',str)
print(ra)
// 输出结果如下
['age is 180 years old,and his weight is about 140 pound.']
['age is 180']
['age is 180']
// 上面第三个匹配多一个问号的区别在于是非贪婪模式,匹配到一个 'his (.*?) years' 就停止匹配;
// 贪婪模式会输出所有符合 'his (.*) years' 的结果
r=re.findall('his (.*?) ',str) // 注意括号后有空格,若去掉空格,则返回为空
print(r)
// 输出结果如下
['age', 'weight']
小心空格,一不小心就成了拦截符!,对非贪婪匹配结果有大影响。
3、字符替换与分割
3.1 方法使用
首先将正则表达式的字符串创建成模式对象,再来使用:
rx = re.compile(pattern, flags)
rx.split(string,maxsplit)
在正则表达式匹配的地方进行分割,并返回一个列表。
rx.sub(replacement, string, count)
返回替换后的字符串,这个字符串从最左边开始,所有 RE 匹配的地方都替换成 replacement
rx.subn(replacement, string, count)
返回值为一个包含有两个元素的元组:替换后的字符串,替换的数目
4、常用的数据格式匹配
邮箱地址(Email)
pattern = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"
域名(domain)
pattern = r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
IP 地址
pattern = r"((?:(?: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)))"
电话号码
pattern = r"\d{3,4}-\d{7,8}"
手机号码
pattern = r"(?:13[0-9]|15[0-9]|17[0678]|18[0-9]|14[57]|09){1}[0-9]{8}"
身份证号码(18 位)
pattern = r"(?:[1-9]\d{5}[12]\d{3}(?:0[1-9]|1[012])(?:0[1-9]|[12][0-9]|3[01])\d{3}[0-9xX])"
中文字符
pattern = r"[\u4e00-\u9fa5]"
欢迎各位看官及技术大佬前来交流指导呀,可以邮件至 jqiange@yeah.net