python之正则表达式
正则表达式是处理字符串的强大工具,可以实现字符串的检索,替换、匹配验证
常用的匹配规则如下:
| 模式 | 描述 |
| \w | 匹配字母、数字、下划线 |
| \W | 匹配不是字母、数字、下划线 |
| \s | 匹配任意空白字符,等价于[\t\n\r\f] |
| \S | 匹配任意非空字符 |
| \d | 匹配任意数字,等价于【0-9】 |
| \D | 匹配任意非数字字符 |
| \A | 匹配字符串开头 |
| \Z | 匹配字符串结尾,如果存在换行,则匹配到换行前的字符串 |
| \z | 匹配字符串结尾,如果存在换行,同时还会匹配换行符 |
| \G | 匹配最后完成匹配的位置 |
| \n | 匹配一个换行符 |
| \r | 匹配一个制表符 |
| ^ | 匹配一行字符串的开头 |
| $ | 匹配一行字符串的结尾 |
| . | 匹配任意字符,除了换行符,如果标记re.DOTALL,则可以匹配换行符 |
| […] | 表示一组字符,单独列出,比如[amk],匹配a、m或k |
| [^…] | 匹配不在[]中的内容,如[^abc]表示匹配除了abc之外的字符 |
| * | 匹配0个或者多个表达式 |
| + | 匹配1个或者多个表达式 |
| ? | 匹配0个或1个前面的正则定义的片段,非贪婪方式 |
| {n} | 精确匹配n个前面的表达式 |
| a|b | 匹配a或b |
| () | 匹配括号内的表达式,也表示一个组 |
| {n,m} | 匹配n到m次由前面正则定义的片段,贪婪方式 |
python中的re库提供了整个正则表达式的实现,下面说下常用方法:
1、match():
向match()方法传入要匹配的字符串以及正则表达式,就可以检验这个正则表达式是否匹配字符串
match()方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,返回匹配成功后的结果,如果不匹配,返回None,如图:

从上图中可以看出,match()方法中,第一个参数传入了正则表达式,第二个参数传入了要匹配的字符串,首先声明字符串content,里面包含字母、空格、数字,match()方法中添加正则,^hello表示匹配以hello开头,\s表示匹配一个空格,\d\d\d表示匹配三个数字,再写一个\s表示匹配一个空格,\d{4}表示匹配4个数字,在通过\s匹配空格,最后通过\w{5}匹配5个字母
注意:\d\d\d 也可以通过\d{3}的形式书写,更加简单方便,还可以写成\d+表示匹配1个或者多个\d
代码执行结果如下:

打印结果中,第一行为字符串的长度,第二行表示match()方法结果为SRE_MATCH对象,其中包含了匹配内容以及匹配范围,第三行为group()方法匹配的内容,第四行为span()方法匹配的范围,也就是字符串的长度
匹配目标
上面通过match()方法获得了匹配的字符串内容,如果想从此内容中提取一部分内容,应该怎么办呢?
此时可以使用()括号将想要提取的的子字符串括起来,()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,然后调用group()方法传入分组的索引即可获得提取结果,如图:

从上图中可以看出,正则表达式中匹配数字的\d+用()标记起来了,此时对应的分组索引为1号分组,如果后面还有多个标记,则对应2号分组,3号分组
代码执行结果如下:

结果可以看出group(1)成功提取了字符串内容,group()会输出完整的匹配内容,group(1)则可以在此内容基础上提取子字符串,也就是被()标记的内容,如果正则表达式后面还有()标记的内容,那么依次用group(2)、group(3)等来获取即可
通用匹配
上面写的正则表达式比较复杂,出现空白字符就用\s匹配,出现数字就用\d匹配,这样写比较复杂,还有一个万能匹配,可以用.*(点和星),其中.(点)可以匹配任意字符(除了换行符),*(星)表示匹配前面的字符无限次,所以他们组合一起可以匹配任意字符
修改上面的例子,通过.*来进行匹配,如图:

上图中的正则表示匹配以hello开头,shijie结尾,中间匹配任意字符的内容
贪婪和非贪婪
使用上面的.*匹配时,有时候匹配到的可能并不是我们想要的结果,如图:

上图中正则匹配使用()标记了一个子表达式用来匹配数字,其余部位用.*匹配任意字符,然后以shijie结尾,在通过group(1) 打印提取内容时得到的结果如下:

从上图中可以看出,匹配的结果为7,与原计划的1234567不符合,为什么呢?这里就涉及到一个贪婪匹配与非贪婪匹配的问题,在贪婪匹配模式下,.*会匹配尽可能多的字符,正则表达式中.*后面是\d+也就是至少匹配一个数字,并没有指定具体多少个数字,因此,.*就尽可能匹配多的字符,这里就将123456匹配了,给\d+留下一个可满足条件的数字7,因此最后得到的内容就是数字7
此时使用非贪婪匹配可以准确的获取到我们想要的内容,非贪婪匹配的写法为.*?,
注意看后面.*后面多了一个问号,通过下面的实例体验下功能:

从上图中可以看出,在.*后面加了一个?即为非贪婪模式匹配,打印结果如下:

上图中成功打印了想要的结果,非贪婪匹配就是尽可能匹配少的字符,当.*?匹配到hello后面的空白字符时,在往后就是数字了,而\d+恰好可以匹配数字,此时.*?将不会进行匹配,而是交给\d+来匹配,因此得到的结果正是想要的结果。
总结:在做匹配的时候,字符串中间尽量使用非贪婪匹配,以免出现匹配结果缺失的情况。
有一种情况要注意:如果匹配的结果在字符串结尾,使用.*?就有可能匹配不到任何内容,如图:

从上图可以看出,两个正则都是匹配abc,一个使用.*?,另一个使用.*,下面通过group(1)调用的时候可以发现,通过.*?匹配的内容为空,因此非贪婪匹配尽可能少的去匹配,而.*尽可能多的匹配,因此可以匹配到,打印结果如下:

修饰符
正则表达式可以包含一些可选的修饰符来控制匹配的模式,修饰符被指定为一个可选的标志,下面例子如下:

上图中,在字符串中加了换行符,正则表达式不变,用来匹配数字,打印结果如下:

从上图可以看出,运行直接报错,因此”.”(点)匹配的是除了换行符之外的任意字符,当遇到换行符的时候,.*?就不能匹配了,所以导致失败,此时可以在后面添加一个修饰符re.S,这个修饰符的作用是使”.”匹配包括换行符在内的所有字符,添加后”.”(点)就可以完成换行匹配了,如下:

打印结果中即可看到已经正确匹配了,如图:

还有一些修饰符,在需要的情况下也可以使用,如下:
| 修饰符 | 描述 |
| re.I | 匹配大小写不敏感 |
| re.L | 做本地化识别(locale-aware)匹配 |
| re.M | 多行匹配,影响^和$ |
| re.S | 使.匹配包括换行符在内的所有字符 |
| re.U | 根据Unicode字符集解析字符,这个标志影响\w、\W、\b、\B |
| re.X | 给与更灵活的格式将正则写的更易于理解 |
转义匹配
我们知道”.”可以匹配除了换行符之外的所有字符,那么如果字符串中包含”.”,此时就需要使用转义字符了,如下:

从上图中可以看出,通过转义字符已经成功匹配到了内容
2、search()
match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了,例如:

上图中使用match()函数,正则从big开始匹配,运行结果如下:

从结果可以看出,从中间匹配不成功,返回结果为None,因此match()方法更适合用来检测某个字符串是否符合某个正则表达式的规则
search()方法在匹配的时候会扫描这个字符串,然后返回第一个成功匹配的结果,正则表达式可以是字符串的一部分,匹配的时候,search()方法会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没找到,就返回None。
将上述例子中的match()方法修改为search()方法,如图:

代码执行结果如下,已经成功打印出结果,如图:

3、findall()
上面介绍了search()方法,它可以返回匹配正则表达式的第一个内容,但是如果要获得


