python 正则表达式
正则字符
- 正则是用来处理文本数据的好帮手,大多数程序和文本工具都支持正则,使用起来也非常方便
- 在实际应用中,处理爬虫的数据解析,日志的分析处理,以及字符串提取关键数据,应用比较普遍
- 在编程中,使用文本编辑器查找或替换关键字,比直接匹配查找更加高效,也更加容易控制。
- 点击查看python元字符使用示例
元字符
- 可以表示数量的元字符
* 0 ,1,2,3,4,5 .....
+ 1,2,3,4,5.....
? 0,1
{2} 2
{0,1} 0,1
{1, } 1,2,3,4,5.....
- 表示一类字符,通常这些字符能够匹配一个位置,但可以匹配多个种类的字符
[a-z] 可以表示从a-z中的任意一个字符
[^a-z] 可以表示从除了abcde...z以外的字符,例如 123ABCD*&@!等等
. [*\s] 表示 abcde...zABCDE...Z01234...9 都可以用 . 来表示
\s [ \n\t\r\f] 表示换行,空格,等空白字符
\w [a-zA-Z0-9_] 表示 abcde...zABCDE...Z01234...9 都可以用 . 来表示
\d [0-9] 表示数字,如果想要匹配带小数点,需要使用[\d\.]+ 或者 [0-9\.]+
\S [^\s]
\D [^\d]
\W [^\w]
" " 空格可直接使用
\n 匹配换行
- 特殊字符(转义字符)
\
上面的提到的字符,如果想用这些字符的原来意思,就需要搭配转义字符
1. 需要匹配 "C:\project\cmp" 和 "C:\windows\cmp", 使用下面的正则表达式
- "C:\\\w+\\cmp"
- 解释下 "C:\\" 匹配 "C:\"
- "\w+" 匹配 project 和 windows
- "\\cmp" 匹配 "\cmp"
2. [] 中表示 - 需要加 \-, 需要匹配 qjn.kjh-123 和 kjojoi-1.23544
- [\w-\.]+ 报错, 因为这样表示 从 \w 到 \. ,但没有这种说法,所以是错误的
- [\w\-\.]+ 正确
- [\w\.-]+ 正确
- 在[]外面直接使用 - 是没问题的
- 特殊字符 (开头和结尾判断)
^$
\A\Z
需要获取,abcd 和 aswd, 但是不匹配 aloj和qwed,也就是需要a开头d结尾的四个字符
- ^a\w{2}d$
- \Aa\w{2}d\Z
- 两者是相同的
- 特殊字符 (或)
|
需要获取 ms-windows32 和 windows64
- (ms-|)windows\d{2}
- (ms-|)表示 ms- 或者没有
- 贪婪与非贪婪模式
需要获取 The cat is fat. 中的 cat
使用 The .*at 就会匹配到 cat is fat
使用 The .*?at 就会精确匹配到 cat
? 表示数量时,是 0 或 1个
跟在数量字符(* +)后面的时候,就是非贪婪模式,意思就是匹配最少的
而贪婪模式相反,意思是匹配最多的字符
以下内容和python一起讲
-
高级使用 (捕获组(簇))
()
- 非捕获组
- 普通捕获组
- 命名捕获组
-
正则匹配设置
python 使用正则表达式(regular expression)
python 使用正则表达式使用
re
库
- search
- findall
- match
- finditer
- 四种使用方式都一样,三个参数
func(regex,string,flags)
- 第一个参数,正则表达式
- 第二个参数,需要被匹配的字符串
- 第三个参数,就是正则匹配设置,该参数可选
import re
print(re.findall("C\d+", "abc1234", flags=2))
- 第一种是我经常使用的一种了, 介绍下search 方法
原:Hello my dear friend!what's up?
目:dear
import re
string_1 = "Hello my dear friend!what's up?"
target_1 = re.search("dear", string_1)
print(target_1.group()) # dear
# 坑1,无法匹配时,使用group函数获取结果,会报错 NoneType 没有 group 函数
string_1 = "Hello my dear friend!what's up?"
target_1 = re.search("dears", string_1)
print(target_1.group()) # 报错
- 第二种在匹配很多相同结构的数据非常有用, 介绍下 findall方法
原: name: 123\nname: 456\nname: 789\nname1: 123
目: name: 123 name: 456 name: 789
import re
string_2 = "name: 123\nname: 456\nname: 789\nname1: 123"
target_2 = re.findall("name: \d+", string_2)
print(target_2) # ["name: 123", "name: 456", "name: 789"] 三个数据
# 问题,为什么不用search 这种方式,OK,使用search 在匹配一次
import re
string_2 = "name: 123\nname: 456\nname: 789\nname1: 123"
target_2 = re.search("name: \d+", string_2)
print(target_2.group()) # name: 123 一个数据
使用search 只会匹配一次,但 findall 会匹配到所有符合这个表达式的字符串
- 第三种方式用的比较少,叫做完全匹配 match
原:123asd456pds
目:123asd456pds
import re
string_3 = "123asd456pds"
target_3 = re.match("\w+", string_3)
print(target_3.group()) # 123asd456pds
此时与search, findall 没什么区别, 换一下原来的字符串
原:?123asd456pds
目:123asd456pds
import re
string_3 = "?123asd456pds"
target_3 = re.match("\w+", string_3)
print(target_3.group()) # 报错,没有匹配到
target_3 = re.search("\w+", string_3)
print(target_3.group()) # 123asd456pds 正常
可以近似地看作 在search的基础上所有匹配都加了 ^$ 判断
python 高级正则表达式
- 首先是捕获组,先看一个例子
原:you are the man,you are the best!
目:man 和 best
string_4 = "you are the man,you are the best!"
target_4 = re.findall("you are the (\w+)", string_4)
print(target_4) # ['man', 'best']
捕获组就是用来精确获取信息的一种方式,因为 you are the 我们是不需要的,但是缺少了,
又没办法匹配到 man 和 best,为了让结果只出现 man 和 best 我们使用捕获组,
结果中就只会有捕获组中的数据了
- 不使用捕获组
string_4 = "you are the man,you are the best!"
target_4 = re.findall("you are the \w+", string_4)
print(target_4) # ['you are the man', 'you are the best'] 不是我们期望的结果
- 非捕获组,使用案例
string_4 = "you are the man,you are the best!"
target_4 = re.findall("you are the (?=\w+)", string_4)
print(target_4) # ['you are the', 'you are the']
虽然使用了 () , 但是 \w+ 匹配到的数据并没有出现在结果中
原:ms-windows32 和 windows64
目:32 和 64
import re
target = re.findall("(?=ms-|)windows(\d+)", "ms-windows32 和 windows64")
print(target) # ['32', '64']
ms- 只是用来判断的,如果不加非捕获组,ms- 就会出现在结果中
使用总结 非捕获组通常和(|) 一起使用
- 命名捕获组
原:Hello my name is pp.
目:pp
string = "Hello my name is pp."
target = re.search("my name is (?P<name>\w+)", string)
print(target.group("name")) # pp
# 典型应用,匹配中使用前一个匹配到的数据
原:<html>你好</html>
目:html
string = "<html>你好</html>"
target = re.search("(<(?P<name>\w+)>.*?</(?P=name)>)", string)
print(target.group("name")) # html
(?P<name>\w+) 命名捕获组
(?P=name) 使用 name 这个命名捕获组捕获到的数据, 两者一致才能正确匹配
该场景在爬虫中使用较多
search 方法中,捕获组的一些常见使用
原:where is your bug?
目:bug
import re
target = re.search("your (\w+)", "where is your bug?")
print(target.group()) # "where is your bug?" ? 为什么结果不对
print(target.group(1)) # bug 正确
print(target.groups()) # ('bug',)
target = re.search("your (?P<name>\w+)", "where is your bug?")
print(target.group("name")) # bug
print(target.groupdict()) # {'name': 'bug'}
- 正则匹配设置
- 忽略大小写
- 多行修饰
.
包含换行符, 即变成所有字符皆可匹配
# 常用情况,忽略大小写
原: abcACBBAC
目: abcACBBAC
target = re.search("[abc]+", "abcACBBAC", flags=2)
print(target.group()) # abcACBBAC 正确匹配
- 如何在findall 中使用 捕获组, 使用finditer
import re
string_4 = "abceee abc12312 abc123.qwe"
target_4 = re.finditer("(?P<xxx>abc\w+)", string_4)
data_list = [i for i in target_4]
print([i.group("xxx") for i in data_list])
# ['abceee', 'abc12312', 'abc123']
print([i.groupdict() for i in data_list])
# [{'xxx': 'abceee'}, {'xxx': 'abc12312'}, {'xxx': 'abc123'}]
print([i.groups() for i in data_list])
# [('abceee',), ('abc12312',), ('abc123',)]
print([i.group(1) for i in data_list])
# ['abceee', 'abc12312', 'abc123']
print([i.group() for i in data_list])
# ['abceee', 'abc12312', 'abc123']
- 注意,finditer 获取到的是迭代器,获取一次后,数据就会清空,需要使用data_list 来保存这一数据
如何学习正则表达式
- 在实际的情况下,使用正则表达式去解决问题,多多使用才能快速掌握
- 下面是我经常碰到的问题
- 不会如何使用,对元字符和函数使用不熟悉
- 写错逻辑,写多了也会,真的,因为实在是太长了
- 正则表达式写的范围太大,匹配很多错误数据
- 正则表达式写的范围太小,错过了很多数据
- 正则表达式太复杂,短时间太难看懂,也难以维护
- 12 问题,通过多加练习,短时间就能掌握
- 34 问题属于两者需要平衡,但我愿意小一点也不愿意匹配到错误数据,但这个还是要视情况而定
- 5这个问题,在非常多的正则表达式的情况下,通过人工维护,不仅费时费力,通常是一个难以解决的问题
- 不过 5 这个问题,可以另辟蹊径,就是把正则表达式制作成一个通用的工具,增加灵活性,脱离正则表达式的范畴