正则表达式,一个强大的规则。在python中,正则表达式涉及的库主要有:xpath、bs4、re 。今天,我们主要讲述re库。
字符串操作是我们不管学习哪种脚本语言,都涉及的。不管是爬虫,还是数据表格处理,都离不开字符串操作。然而,在字符串章节的内容中,我们已经知道,字符串内建函数也是挺丰富的。一定程度上,也能够满足我们的项目需求。但是,学会了正则,我们在字符串处理上面,更加灵活自如,如鱼得水。
原文链接:https://blog.csdn.net/weixin_44051713/article/details/93162249 此文转载,仅用于个人学习,感谢大佬分享!!!
一 正则表达式是什么
正则表达式是一组特殊的字符序列、是对字符串操作的一种逻辑公式。
正则表达式通常被用来检索、替换那些符合某个模式的文本。比如检测一串数字是否符合电话号码、检测一个字符串是否符合email标准,实现诸如此类的对于字符串或者是文本的操作都可用正则表达式实现,正则表达式的功能是非常强大的,掌握正则表达式也是非常重要的。
二 元字符是什么
首先,
import re
string='JavaScript|.Net|Java|Python'
re.findall("正则表达式",string)
在上面代码中,如果我们想要匹配到Python,我们直接可以在正则表达式中直接写入Python,他便会返回一个结果列表,这就是正则最基本的用法,这样的结果并没有意义,因为我们填写的是一个常量字符串,并没有把正则的规则和意义体现出来,正则表达式最重要的就是规则。
其次,
import re
string='Pyth4on|J5av2a12Script|5.Net|Ja2va'
re.findall("正则表达式",string)
现在有这么一个问题,要求在string字符串中匹配出所有的数字,那么如果我们用刚刚的常量字符串,那么只能写成:
re.findall("0",string)
re.findall("1",string)
re.findall("2",string)
...
最后,
这样显然也是没有意义的,那该怎么办呢,正则表达式提供了一个抽象的符号 ‘/d’ 代表0-9的任何数字。
import re
string='Pyth4on|J5av2a12Script|5.Net|Ja2va'
result=re.findall("\d",string)
print(result)
总结,
这样 我们就把该字符串里的所有字符匹配出来了,然后我们回过头来思考一下正则表达式的定义,正则表达式是一组特殊的字符序列,所以正则表达式最终要落入字符这里,之前的我们写的直接匹配Python的字符串,我们的正则表达式就会给他构造一个类型 叫做普通字符 对于这样的一个 ‘/d’ 我们把它叫做元字符 我们的正则表达式就是用一系列的普通字符和元字符组成的,普通字符并没什么可讲性,他就是你看到这样的实实在在的字符,它就是具体的字符,所以我们应该主要学习的就是元字符。
正则表达式中有特别多的元字符 如下图(简略的介绍):
如此多的元字符,是不是头皮发麻了,其实千万不要想着把这些背下来,我们只要根据自己的业务需求,然后将元字符组合成你需要的就可以了,其实日常用的元字符并不多。
三 字符集
学习了元字符相信大家对正则表达式有初步了解.
接下来我要讲解的知识点就是:正则表达式主要模式之一,字符集 接下来看一个例子:
import re
string='cat,dog,bat,log,sit,pig'
result=re.findall("正则表达式",string)
print(result)
要求:匹配出所有中间字母为o或i的字母,重点是这个 或 字。
很明显我们不能使用普通字符来匹配,所以我们现在又要用一种抽象来概括 o 和 i 这就是我们字符集的使用,其实很简单:
import re
string='aac,abc,aic,aoc,azc,awc'
result=re.findall("a[io]c",string)
print(result)
我们的字符集用的是 [ ] ,然后把我们要抽象的字符集写在中括号里。这里我们还用了普通字符,如果没有普通字符来给我们定界的话,我们无法直接筛选出我们想要的字符的(大家可以试一下) 下面跟大家解释一下字符集的特性:
(1)出现在中括号里面的字符是 或 关系
(2)他还有另外一个用法:比如匹配除了中间字符为i和o的字符 我们只需要在中括号里面加一个 上尖角 ^
(3)还要一个小技巧,比如我们要匹配的字符太多的话,比如要匹配 bcdefghi 这样的字符集 我们可以直接在中括号里面写 [b-i] 他就可以这这些全部匹配出来了。
四 概括字符集应用
什么叫概括字符集呢?
比如我们之前学的元字符 \d 他就是一个概括字符集 他可以用字符集写成[0-9] \d就是对他的一种概括(其实有很多叫法) 因为正则表达式是非常灵活的,比如我们刚刚做的匹配数值的哪一题也可以写成:
import re
string='Pyth4on|J5av2a12Script|5.Net|Ja2va'
result=re.findall("[\d]",string)
print(result)
五 数量词应用
在我们下列案例中有这么一个问题:
import re
string='Ja5va|Pyth4on|JavaSc1ript|.Ne6t'
result=re.findall("[a-z]",string)
print(result)
输出#['a', 'v', 'a', 'y', 't', 'h', 'o', 'n', 'a', 'v', 'a', 'c', 'r', 'i', 'p', 't', 'e', 't']
你会发现他会把这些单词拆分成一个字母,不管是字符集也好还是元字符也好,他们只能表示一个字符,那如何合成一个单词呢,可能有些人会想出这种办法:
import re
string='python 11java899php'
result=re.findall("[a-z][a-z][a-z]",string)
print(result)
#['pyt', 'hon', 'jav', 'php']
多写几个字符集
那么如果是100个字符呢,所以这种方式肯定是不可取的
那该怎么办呢,这就引出了我们的数量词:
import re
string='python 11java899php'
result=re.findall("[a-z]{3}",string)
print(result)
#['pyt', 'hon', 'jav', 'php']
这就是数量词的用法,他可以吧前面的字符重复N次或者N到M次
在上面案例中 花括号3,6 表示匹配符合规则的字符3-6次 其实当匹配到第三次的时候就已经符合匹配规则了,那么他为什么还要继续匹配呢???
这就涉及到一个比较重要的概念(贪婪和非贪婪),接下来我会讲解。
六 贪婪与非贪婪
什么是贪婪模式?
比如我们把匹配规则定义在一个区间内 他会尽可能多的去匹配符合规则的字符。
非贪婪反之。
python默认的匹配模式是贪婪模式
非贪婪如何表示?
在数量词后面添加一个"?" 这样就表示非贪婪模式 如下(同样是上一题):
import re
string='python 11java899php'
result=re.findall("[a-z]{3,6}?",string)
print(result)
#['pyt', 'hon', 'jav', 'php']
在原理上很简单,但是在具体正则使用的时候贪婪和非贪婪会经常产生一些程序bug的,这个要着重注意的,特别的入门不久的新手
数量词还包括 *(匹配前面字符0次或多次) +(匹配前面字符1次或者多次)
七 三方库re提供的一些方法
注:
re模块的正则表达式相关函数中都有一个flags参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。
其中一个常用可选参数,flags=re.IGNORECASE
,表示匹配的时候忽略字符串大小写。如:
方法名字 | 举例 | 注意事项 |
re.match() | match(pattern, string) | 从字符串的起始位置开始匹配,如果匹配成功,则返回匹配字符串。如果匹配不成功,则返回None。注意:一定是从头开始匹配,中间匹配成功不算。然而前面匹配到了后,后边多出来的字符就不管了。 print(re.match(r'The', 'The world is ...')) 结果: |
re.fullmatch() | fullmatch(pattern, string) | 字符串从头匹配到尾,匹配成功才会返回正则表达式对象。也就是说,后边多出来的字符串他也要管,发现没匹配到,直接返回None。 实例: print(re.fullmatch(r'The', 'The world is ...')) None |
re.search() | search(pattern, string) | search和findall和上边的match和matchall的最重要差别是,search和findall不管头不管尾,只要在中间有地方匹配到了,就匹配成功了,看这方法名也可以看出匹配的严格程度较低。 # 下面正则表达式的意思是,先匹配一个下划线,然后匹配一个任意的(字母/数字/下划线) print(re.search(r'_\w', 'The_world_is_...')) <re.Match object; span=(3, 5), match='_w'> |
re.compile() | re.compile(pattern, flags=0) | pattern 指定编译时的表达式字符串 re.I(re.IGNORECASE) :使匹配对大小写不敏感 |
re.findall() | findall(pattern, string) | search和findall和上边的match和matchall的最重要差别是,search和findall不管头不管尾,只要在中间有地方匹配到了,就匹配成功了,看这方法名也可以看出匹配的严格程度较低。 print(re.findall(r'_\w', 'The_world_is_...')) ['_w', '_i'] |
re.finditer(pattern, string[, flags]) | 搜索string,返回一个顺序访问每一个匹配结果的迭代器 | |
re.sub | re.sub(pattern, repl, string, count=0, flags=0) | 对于输入的一个字符串,利用正则表达式(的强大的字符串处理功能),去实现(相对复杂的)字符串替换处理,然后返回被替换后的字符串。
|
re.subn | re.sub(pattern, repl, string, count=0, flags=0) re.subn(pattern, repl, string, count=0, flags=0) | 参数: 1)pattern:匹配的正则表达式; 2)string:搜索文本字符串 3)repl:替换对象,将搜索文本中和模式匹配的字符串替换为repl对象对应的内容,repl可以是,或者是字符串一个函数 4)count:用于限制最多替换多少次,为0或者没有传入则全部替换; 5)flags:搜索标记,与《第11.2节 Python 正则表达式支持函数概览》介绍的参数flags标记含义相同。 返回值: sub返回通过使用 repl 替换在通过正则表达式模式搜索到的字符串。 如果没有找到匹配子串,则直接返回 string。 subn与sub函数一样执行搜索替换,但是不是返回替换后的字符串,而是返回一个元组,元组的第一个元素是替换后的字符串(该元素与sub返回值相同),第二个元素是执行替换的次数。 |
re.split | split(pattern,string) |
# 这里正则表达式的意思是,先匹配一个下划线后再匹配一个任意字符 结果: ['The', 'orld', 's', '..'] |
re.group | 1 >>> a="123abc456" 2 >>> import re 3 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0)) 4 123abc456 5 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1)) 6 123 7 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2)) 8 abc 9 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(3)) 10 456 11 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group()) 12 123abc456 13 >>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups()) 14 (‘123‘, ‘abc‘, ‘456‘) 15 >>> print(re.search("([0-9])*([a-z])*([0-9]*)", a).groups()) 16 (‘3‘, ‘c‘, ‘456‘) 17 >>> print(re.search("([0-9])*([a-z])*([0-9]*)", a).groups(1)) 18 (‘3‘, ‘c‘, ‘456‘) 19 >>> print(re.search("([0-9])*([a-z])*([0-9]*)", a).group(0)) 20 123abc456 21 >>> print(re.search("([0-9])*([a-z])*([0-9]*)", a).group()) 22 123abc456
| 在正则表达式中,re.group()方法是用来提取出分组截获的字符串,匹配模式里的括号用于分组. #!/usr/bin/env python 结果: 18年2019年2020年 |