正则表达式是一个通用概念,不管你使用的编程语言是Python还是Java亦或C#。python里实现正则的模块是re,使用前需导入。
一、语法
1.基本元素
字符 | 含义 |
. | 匹配除换行符以外任意字符 |
\w | 字母、数字、下划线、汉字 |
\W | 排除\w |
\d | 数字 |
\D | 排除\d,即非数字 |
\s | 空白符 |
\S | 非空白符 |
^ | 字符串的开始 |
$ | 字符串的结束 |
\b | 单词的开始或结束 |
\B | 非单词边界 |
[] | 字符集 |
[^] | 排除集 |
| | 或 |
\ | 转义 |
() | 分组 |
2.数量限定符
符号 | 说明 |
? | 匹配零次或一次 |
+ | 一次或多次 |
* | 零次或多次 |
{n} | n次 |
{n,} | 至少n次 |
{m, n} | 至少m次,至多n次 |
二、re模块
1.从开头进行匹配——re.match
相当于你的正则表达式要和整个字符串匹配,遇到不匹配的字符直接返回None。
import re
s = '23ab45cd'
p = r'^\d+'
res = re.match(p, s)
if res:
print('匹配开始位置:', res.start())
print('匹配结束位置:', res.end())
print('匹配结果:', res.group())
# 输出:匹配开始位置: 0
匹配结束位置: 2
匹配结果: 23
2.匹配第一个符合的字符串片段——re.serach
搜索整个字符串,直到遇到第一个匹配的值。如果没有匹配值,返回None。
s = '@23ab45cd'
p = r'\d+'
res = re.search(p, s)
if res:
print('匹配结果:', res.group())
# 输出:匹配结果: 23
3.匹配所有符合的值——re.findall
返回所有的匹配结果,以列表形式返回。
s = '@23ab45cd'
p = r'\d+'
res = re.findall(p, s)
if res:
print('匹配结果:', res)
# 输出:匹配结果: ['23', '45']
4.使用正则替换字符串——re.sub
简单理解就是替换符合表达式的一类字符。
# 将数字替换为'xx'
s = '@23ab45cd'
p = r'\d+'
res = re.sub(p, 'xx', s)
print('替换结果:', res)
# 输出:替换结果: @xxabxxcd
5.使用正则分割字符串——re.split
即分割符是符合表达式的一类字符
# 以数字作为分隔符
s = '@23ab45cd'
p = r'\d+'
res = re.split(p, s)
print('分割结果:', res)
# 输出:分割结果: ['@', 'ab', 'cd']
6、标志——flag
标志 | 含义 |
A或ASCII | 只进行ASCLL匹配 |
I或IGNORECASE | 忽略大小写 |
M或MULTILINE | 将^和$作用于每一行的开头和结尾(针对多行) |
S或DOTALL | “."将匹配所有字符,包括换行符 |
X或VERBOSE | 忽略模式字符串中未转义的空格和注释 |
s = '我am a 码农'
p = r'\w+'
# 早期ASCLL编码只包含英文字母和一些符号
res = re.findall(p, s, re.A)
# python3采用Unicode编码,中英法等都会被认为是“字母”,即都能被\w匹配
res2 = re.findall(p, s)
print('只匹配ASCLL结果:', res)
print('默认匹配结果:', res2)
# 输出:只匹配ASCLL结果: ['am', 'a']
默认匹配结果: ['我am', 'a', '码农']
三、综合使用
1.字符串定位符^$和单词首尾定位\b的区别
字符串就是一对引号里的内容,^和$默认只匹配整个字符串的开始和结尾,如果使用标志M,则会匹配多行字符串每一行的开始和结尾。
s = '123@abc_111.com\n456@abc_111.com'
p = r'^.+$'
# 相当于换行符的前后会被认为是字符串的首尾
res = re.findall(p, s, re.M)
# 默认作用于整个字符串首尾,首尾之间有换行符,因此"."不能匹配
res2 = re.findall(p, s)
print('匹配多行结果:', res)
print('默认匹配结果:', res2)
# 输出:匹配多行结果: ['123@abc_111.com', '456@abc_111.com']
默认匹配结果: []
单词的定义可以理解为\w的范围(字母数字下划线)。\b则去匹配逗号分号等各种符号。
s = '123@abc_111.com 456@abc_111.com'
p = r'\b\w+\b'
res = re.findall(p, s)
print('匹配结果:', res)
# 可以理解为@.和空格被作为边界
# 输出:匹配结果: ['123', 'abc_111', 'com', '456', 'abc_111', 'com']
2.()和"|"的用法
# 这里并非完整实现邮箱匹配,仅限制开头是数字+@
# @后面接2个字母或3个数字
s = '123@qq.com\n123@163.com'
p = r'^\d+@([a-z]{2})|(\d{3}).*$'
res = re.findall(p, s, re.M)
print('匹配结果:', res)
# 当匹配123@qq.com时,一个分组匹配成功,结果是'qq';第二个分组匹配失败,因此结果是空字符。
# 还有一个要点,返回结果并不是完整的邮箱,只返回分组内匹配成功的元素
# 输出:匹配结果: [('qq', ''), ('', '163')]
如果想得到完整的邮箱,可以将你想要的信息再通过一个()包含进去即可
s = '123@qq.com\n123@163.com'
p = r'^(\d+@([a-z]{2})|(\d{3}).*)$'
res = re.findall(p, s, re.M)
print('匹配结果:', res)
# 输出
匹配结果: [('123@qq.com', '', '123'), ('123@163.com', '', '123')]
3.()\数字
如()()\2,表示(第一组)(第二组)(第二组),即\2指代第二组
s = '123@qq.com_qqcom'
# 表示任意长度>=3的子字符串为一组,后面可能接其他元素,
# 再后面是任意长度>=2的子串作为第二组,然后重复第一组
p = r'(.{3,}).*(.{2,})\1'
res = re.findall(p, s)
print('匹配结果:', res)
# 输出: 匹配结果: [('com', 'qq')]
4.转义
正则表达式中pattern是作为模式字符串,因模式字符串需要使用大量反斜线和特殊字符,为了避免和转义字符混淆,一般在其前面加一个r或R。
print(r'不进行\n转义')
print('进行\n转义')
# 输出:不进行\n转义
进行
转义
5.贪婪和非贪婪模式
贪婪模式:尽可能多的匹配,包括+, ?, * , {n,}, {n,M}
非贪婪模式:尽可能少的匹配,在贪婪模式的基础上加一个"?",如+?, ??, *?
s = 'abc123ABC123'
p = r'[a-z]+'
p2 = r'[a-z]+?'
res = re.match(p, s)
res2 = re.match(p2, s)
print('贪婪匹配结果:', res.group())
print('非贪婪匹配结果:', res2.group())
# 输出:贪婪匹配结果: abc
非贪婪匹配结果: a
6.匹配中文
s = 'abc123中文123汉字'
p = r'[\u4e00-\u9fa5]+'
res = re.findall(p, s)
print('匹配结果:', res)
# 输出:匹配结果: ['中文', '汉字']