正则表达式(一)
应用场景
- 数据验证:检查字符串是否符合特定的格式,如电子邮件地址、电话号码、URL、IP地址、日期格式等。
- 数据抽取:从文本中提取所需的信息,如提取日志文件中的日期、错误代码或者提取网页中的链接。
- 文本替换:在文本编辑器中进行查找和替换操作,或者在编程中对字符串进行复杂的替换,如大小写转换、删除多余空格等。
- 文本分割:使用复杂的分隔符或模式对字符串进行分割,得到字符串数组。
- 字符串解析:解析复杂格式的字符串,如解析自定义的配置文件内容。
- 语法高亮:在编程开发环境中,对代码进行语法高亮显示。
- 自然语言处理:在自然语言文本中识别句子、单词、短语的模式,如词干提取、词性标注等。
- Web爬虫:从网页源码中提取所需的数据,如图片链接、特定的文本信息等。
- 文件重命名:在文件管理中,对大批量文件进行基于模式的重命名操作。
- 日志分析:提取、分析服务器日志文件中的关键信息,以便进行故障排除、性能监控等。
使用原则
- 简单化:尽可能使用简单的表达式。复杂的表达式可能难以理解和维护,并且可能导致性能问题。
- 明确性:你的正则表达式应该明确无误地匹配你想要的文本,不应该匹配到错误的文本。
- 避免过度使用:对于一些简单的字符串操作,如简单的查找、替换或分割,使用基本的字符串函数会更高效。
- 避免贪婪匹配:默认情况下,正则表达式的量词(如 *和+)是贪婪的,它们会尽可能多地匹配字符。在许多情况下,使用非贪婪量词(如*?和+?)可以提高效率并防止意外匹配。
- 重用模式:编译常用的正则表达式,使用 re.compile()函数。这样可以提升性能,因为编译后的模式可以重复使用。
- 注释和文档化:复杂的正则表达式应该有注释说明每个部分的功能,使得其他开发者(或未来的你)可以理解它的工作原理。
- 测试:对正则表达式进行彻底测试,确保它们在各种情况下都按预期工作,并处理好边缘情况。
- 考虑可读性:尽管正则表达式可能非常强大,但它们也可以变得难以读懂。尽量写出易于理解的表达式,或将复杂的表达式分解成几个小的、管理得当的部分。
- 避免过度匹配:确保正则表达式不会匹配到超出预期范围的文本。使用锚点(如 ^和$)来限定匹配的开始和结束位置。
- 性能考量:对于要在大量文本上执行的正则表达式,要考虑其性能影响。在可能的情况下,使用字符串的内建方法,如 startswith(),endswith(),find()或in运算符,因为它们通常比正则表达式更快。
基本使用
- 
说明
- 
相关函数- 
match- 从开头进行匹配,找到一个立即返回正则对象的结果,没有返回None1 
 2
 3
 4
 5# pyhton 依赖此模块完成正则功能 
 import re
 # 从开头进行匹配,找到一个立即返回正则对象的结果,没有返回None
 m = re.match('abc', 'abchelloabc')
- 可以使用 group(num) 或 groups() 匹配对象函数来获取匹配表达式
匹配对象方法 描述 group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 
 
- 从开头进行匹配,找到一个立即返回正则对象的结果,没有返回None
- 
search- 匹配全部内容,任意位置,只要找到,立即返回正则结果对象,没有返回None
- re.match与- re.search的区别:
 - re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而- re.search匹配整个字符串,直到找到一个匹配。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- # pyhton 依赖此模块完成正则功能 
 import re
 # 匹配全部内容,任意位置,只要找到,立即返回正则结果对象,没有返回None
 m = re.search('abc', 'helloabc')
 if m:
 print('ok')
 # 获取匹配的内容
 print(m.group())
 # 获取匹配的位置
 print(m.span())
- 可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式
匹配对象方法 描述 group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 
 
- 
findall- 匹配所有内容,返回匹配的结果组成的列表,没有返回None
- 多个匹配模式,返回元组列表1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11import re 
 # 匹配所有内容,返回匹配的结果组成的列表,没有返回None
 f = re.findall('abc', 'abcddgasabckjjghlabc')
 if f:
 print(f)
 # 多个匹配模式,返回元组列表
 result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
 print(result)
 # [('width', '20'), ('height', '10')]
 
- 
compile- 
根据字符串生成正则表达式的对象,拥有特定正则匹配,通过match 、search、findall匹配 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11import re 
 # 根据字符串生成正则表达式的对象
 c = re.compile('abc')
 # 然后进行特定匹配
 # m = c.match('abcdefghigl')
 m = c.search('abcdefghigl')
 if m:
 print(m)
 print(c.findall('abcfdasdssabc'))将re模块中的match 、search、findall方法的处理过程分为了两步完成 
 
- 
 
- 
- 
单个字符模式 描述 普通字符 就是一对一的完全匹配 . 匹配除了换行( '\n')的任意字符[] 匹配[]中间的任意一个字符 [a-z] 匹配a到z的任意字符 [0-9] 匹配0到9的任意字符 [^abc] 匹配除abc之外的任意字符 \d 匹配任意数字,等价于 [0-9] \D 匹配任意非数字字符,等价于 [^0-9]\w 匹配数字、字母、中文、下划线等(就是字的意思) \W 匹配\w之外的所有字符 \s 匹配任意空白字符,如:空格、\t、\n、\r、\f \S 匹配任意非空字符,即:\s之外的所有字符 \b 匹配一个单词边界,如:开头、结尾、标点、空格等 \B 匹配非单词边界 
- 
次数控制模式 描述 * 前面的字符可以是任意次,正则的匹配默认是贪婪的 + 前面的字符串至少是一次 ? 匹配至多一次,0次或1次 {m} 匹配固定的m次 {m,} 匹配至少m次 {m,n} 匹配m到n次,在m和n之间尽量取多 {m,n}? 匹配m到n次,在m和n之间尽量取少 正则的匹配默认是贪婪的 
- 
边界限定模式 描述 ^ 以指定内容开头 $ 以指定内容结尾 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14import re 
 # 以指定内容开头
 # c = re.compile(r'^abc')
 # 以指定内容结尾
 # c = re.compile(r'abc$')
 # 同时限制开头和结尾
 c = re.compile(r'^abc$')
 s = c.search('abc')
 if s:
 print('ok')
 print(s.group())
- 
分组匹配模式 描述 a|b 匹配a或b,具有最低的优先级 (re) 表示一个整体,可以确定优先级;对正则表达式分组并记住匹配的文本 (?:re) 类似 (re),但是只表示一个整体,不表示分组 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14import re 
 # | 表示或,具有最低的优先级
 # () 表示一个整体,可以确定优先级
 # () 还有分组匹配的作用,下次再讲
 # c = re.compile(r'ab|cd')
 # c = re.compile(r'a[bc]d')
 c = re.compile(r'a(hello|world)d')
 s = c.search('ahellod')
 if s:
 print('ok')
 print(s.group())
- 
断言- 
正向先行断言- 语法:X(?=Y)
- 含义:匹配 X,且后面跟着Y,但不把Y包含在匹配结果中。
- 例子:1 
 2
 3
 4
 5
 6
 7import re 
 text = "apple pie"
 # 查找所有后面跟着 ' pie' 的 'apple'
 pattern = r"apple(?= pie)"
 print(re.findall(pattern, text)) # 输出 ['apple']
 # 这里匹配“apple”,但要求它后面紧跟“ pie”。“ pie”不算作匹配内容。
 
- 语法:
- 
负向先行断言- 语法:X(?!Y)
- 含义:匹配 X,且后面不跟着Y。
- 例子:1 
 2
 3
 4text = "apple pie and apple tart" 
 pattern = r"apple(?! pie)"
 print(re.findall(pattern, text)) # 输出 ['apple']
 # 匹配所有“apple”,但排除后面跟着“ pie”的。
 
- 语法:
- 
正向后行断言- 语法:(?<=Y)X
- 含义:匹配 X,且X前面紧跟着Y。
- 例子:1 
 2
 3
 4
 5text = "apple pie and banana pie" 
 pattern = r"(?<=apple )pie"
 print(re.findall(pattern, text)) # 输出 ['pie']
 # 这里匹配所有前面是“apple ”的“pie”。
 # 注意: Python 的正则要求正向后行断言中的模式长度必须固定(不能是变长的)。
 
- 语法:
- 
负向后行断言- 语法:(?<!Y)X
- 含义:匹配 X,且X前面不是Y。
- 例子:1 
 2
 3
 4text = "apple pie and banana pie" 
 pattern = r"(?<!apple )pie"
 print(re.findall(pattern, text)) # 输出 ['pie']
 # 匹配所有“pie”,但排除前面是“apple ”的。
 
- 语法:
- 
总结断言类型 语法 描述 例子匹配情况 正向先行断言 X(?=Y)匹配 X,但要求后面跟着 Y "apple(?= pie)"匹配“apple”,后跟“ pie”负向先行断言 X(?!Y)匹配 X,但后面不能跟 Y "apple(?! pie)"匹配不跟“ pie”的“apple”正向后行断言 (?<=Y)X匹配 X,前面紧接着 Y "(?<=apple )pie"匹配前面是“apple ”的“pie”负向后行断言 (?<!Y)X匹配 X,前面不是 Y "(?<!apple )pie"匹配前面不是“apple ”的“pie”
 
- 
练习
- 若匹配有特殊正则含义的字符怎么办,如:\d
- 友情提醒:转移问题(python中转义一次、正则解析转义一次)
 
- 验证一个字符串是否是正确的邮箱格式
- 验证一个字符串是否是正确的url格式
思路
- 以功能为向导
- 写出符合规则的若干字符串
- 写一点测一点,不断调整
- 最终完成功能