作者在学正则表达式的时候多走了很多弯路,在网上也看了很多文章。这篇文章的目的就是让大家少走一些弯路,争取看完这篇文章就能对正则表达式有一个正确的认识,并且能够初步掌握正则表达式。当然那么多表达式是不可能一下就记下来的,因此这篇文章也相当于作者的一个笔记,以后需要的时候可以回来看看。
正则表达式
正则表达式是什么
在我们日常生活中,经常会遇到需要查找某些复杂字符串的情况,比如我们在一篇文章中查找所有的形如010-12345678的电话号码,这种字符串都是三位数字加上‘-’再加上八位数字的形式,这时候就需要用到正则表达式了。在我的简介中,正则表达式是一个模板,所有符合模板形式的字符串都会被它捕获。正则表达式中有一个很重要的定义叫做元字符,例如‘\b’,‘\w’等,在下面会介绍这些元字符使用来干什么的以及怎么使用。
元字符
字符 | 说明 | 实例 |
---|---|---|
. | 匹配除了换行符之外的任意字符 | b.t可匹配bat,but等 |
\w | 匹配字母或数字或汉子或下划线 | b\wt可匹配bat,b1t等 |
\s | 匹配空白符(空格、TAB、换行符) | b\st可匹配b t |
\d | 匹配数字 | b\dt可匹配b1t等 |
\b | 匹配单词的边界 | \bgiao\b匹配单词giao |
^ | 匹配字符串的开始 | ^The匹配The开始的字符串 |
$ | 匹配字符串的结束 | ing$匹配以ing结尾的字符串 |
字符转义
因为“.” ,“\” ,“*”等元字符在正则表达式中都有自己独特的含义,因此我们如果查找它们就容易发生混淆,因此我们如果要查找它们就在它们前面加上一个转义符“\”。要查找‘.’你就写‘.’,以此类推。
重复
符号 | 说明 |
---|---|
1. | 重复零次或更多次 |
2. | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
下面我会用一些例子来使这易于理解。
如果我们想查找一个开头是ac,结尾是ing,中间任意几位字母或数字的字符串,我们的正则表达式应该是ac\w*ing
如果我们想匹配刚好6位的单词,我们的正则表达式应该是\b\w{6}\b
如果我们相想匹配QQ号(5到10位),我们的正则表达式应该是^\d{5,10}$
结合例子来看应该是很易于理解的
字符类
但是如果我们想查找一些没有预先定义的字符,比如我们既就要查找一串‘-=+*’的字符串,我们应该怎么办。很简单,直接用[-=+*]表示即可。
比如我们要查找形如(010)-12345678的字符串,我们的正则表达式应该是这样的(\w{3}[)-]\w{8},这也是易于理解的
分支条件
有时候我们需要查找多种条件下的字符串,这时候我们的正则表达式就需要使用分支条件‘|’.其实很容易理解,我举个例子你就明白了
我们想要匹配两种号码,一种是三位区号加号码012-12345678,另一种是四位区号加号码0123-12345678,正则表达式应该是这样的
0\d{2}-\d{8}|0\d{3}-\d{8}。只需要在两个正则表达式中间加一个|即可
分组
在之前我们已经学会了令单个字符进行重复,但是我们如果想让多个字符进行重复,我们就需要使用到分组了。分组其实就是用‘()’把一些字符括起来。
例子:**(\d{1,3}.){3}\d{1,3}**这个正则表达式表示的是一个简易的地址表示,它的结构是1-3位数字加英文句号重复三次,再加上1-3位数字
反义
之前我们会匹配数字了,用\d就行,但是如果我要你查找所有非数字,应该怎么做呢,这时候就需要用到反义。反义其实很简单,你想要谁的反义就将它的元字符大写,当然也有例外。
符号 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
就像表格写的一样,意思很易于理解
后向引用
不咋好理解,只有几种用法记下来就行。
重复搜索前面某个分组内的文本,\1代表1分组,同理\2就代表2分组,
简单来说组号的分配就是从左向右扫一遍依次编号,但是实际上是扫两遍的,第一遍先扫未命名分组,第二遍再扫命名分组,所以命名分组的组号总是比未分组的组号高。
代码/语法 | 说明 |
---|---|
(exp) | 匹配exp,并捕获文本到自动命名的组里 |
(?exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name’exp) |
(?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 |
接下来将对这三种情况一一进行解释
(1)还是从例子来说:\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。怎么理解这个表达式呢,首先\b(\w+)\b表示的是一个单词,括号中的东西也就被标记为了1分组,之后的\s+表示一个或多个空格,\1表示的自然就是1分组。这是没有命名的分组
(2)要指定一个子表达式的组名,请使用这样的语法:(?\w+)(或者把尖括号换成’也行:(?‘Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k,所以上一个例子也可以写成这样:
\b(?\w+)\b\s+\k\b。
(3)(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。
零宽断言
代码 | 说明 |
---|---|
(?=exp) | 匹配exp前面的位置 |
(?<=exp) | 匹配exp后面的位置 |
例子真是万金油。。。还是用例子来进行说明
(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc。
(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。
负向零宽断言
零断宽言是我们用来匹配“是某字符串exp”的,那么倘若我们想匹配“不是某字符exp”的,我们就要用到负向零宽断言了。
代码 | 说明 |
---|---|
(?!exp) | 匹配后面跟的不是exp的位置 |
(?<!exp) | 匹配前面不是exp的位置 |
(?!exp)叫做零宽度负预测先行断言,是匹配断言位置后面包含exp的,例子:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字。
(?<!exp)叫做零宽度负回顾后发断言,跟上面那家伙相反。例如:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。
注释
一种语法:用“(?#comment)”来包含注释。例如:2[0-4]\d(?#200-249)|250-5|[01]?\d\d?(?#0-199)。括号里的东西也就被注释掉了。
贪婪匹配与懒惰匹配
我们要知道,如果我们的正则表达式是a.b,那么系统会匹配a和b中间最长的一个字符串,这也就是所说的贪婪匹配
但是有时候我们想要尽可能短的,那就需要让它懒惰下来,我们只需要在其后面加一个?就行,a.?b就会匹配尽可能短的字符串,这就是懒惰匹配
平衡组
鹅不会,自行百度,其实学到这你已经基本掌握了正则表达式的内容了。。。。
Python中使用正则表达式
Python提供了re模块来进行正则表达式的相关操作,下面是re模块的核心函数
函数 | 说明 |
---|---|
compile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 |
match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None |
search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None |
split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 |
sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 |
fullmatch(pattern, string, flags=0) | match函数的完全匹配(从字符串开头到结尾)版本 |
findall(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回字符串的列表 |
finditer(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回一个迭代器 |
purge() | 清除隐式编译的正则表达式的缓存 |
re.I / re.IGNORECASE | 忽略大小写匹配标记 |
re.M / re.MULTILINE | 多行匹配标记 |
下面给出一个例子,剩下的还需要多动手来进行参悟
"""
验证输入用户名和QQ号是否有效并给出对应的提示信息
要求:用户名必须由字母、数字或下划线构成且长度在6~20个字符之间,QQ号是5~12的数字且首位不能为0
"""
import re
def main():
username = input('请输入用户名: ')
qq = input('请输入QQ号: ')
# match函数的第一个参数是正则表达式字符串或正则表达式对象
# 第二个参数是要跟正则表达式做匹配的字符串对象
m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$', username)
if not m1:
print('请输入有效的用户名.')
m2 = re.match(r'^[1-9]\d{4,11}$', qq)
if not m2:
print('请输入有效的QQ号.')
if m1 and m2:
print('你输入的信息是有效的!')
if __name__ == '__main__':
main()
例子来源自https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/12.%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%92%8C%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.md