你好,我是
goldsun
。
让我们一起进步吧!
对于大部分“老手”来说,肯定都知道正则表达式这一非常重要的工具啦,而对一些新手而言,可能仅仅听过正则表达式的名字,对其具体有什么用处可能并不了解。其实简单来说,正则表达式就是帮助我们可以从字符串中提取出我们想要的东西的一个工具。(其实它还有一些别的用处,比如使用正则表达式来判断某一字符串符合标准等等)
使用正则表达式,掌握其中的元字符是非常重要的一环,因为正则表达式就是用来匹配具有某个标准的小字符串,而这个标准就是由一个个小标准组合形成的。
举个例子:加入你想匹配出一个字符串中的所有QQ号,那么你可能会去研究QQ号的组成,我们假设QQ号在原字符串中是长为5-11个数字且开头数字不为0的一串字符,那么你可能会这么写正则表达式:
r'[1-9]\d{4,10}'
如果你想检测某个字符串是不是符号要求的字符串,你可以写成:
r'^[1-9]\d{4,10}$'
这样写则是针对检测的字符串整体的。
简单说明下后面这个正则表达式,首先最前边的r
说明这是一个原生字符串,即没有转义字符,其中的\
仅仅代表它本身,这是在python里面的推荐写法而并不是正则表达式的要求,^
是一个元字符,它的作用是匹配字符串的开始,其后跟了[1-9]
,则^[1-9]
将作为一个整体,[1-9]
的意思是匹配1-9之间的任一个字符,那么^[1-9]
的意思就是要匹配的“字符串”的开头是由1-9之间的一个数,后面的\d
也是一个元字符,意思是匹配任意一个数字,其和{4,10}
组成的\d{4,10}
的意思是匹配一段数字,数字的长度为4-10个,最后是一个$
,它的意思和^
类似但相反,它是字符串的结尾,\d{4,10}$
的意思就是要匹配的“字符串”的结尾是4-10个任意数字。
在这里仅仅提到了几个元字符,而在正则表达式中元字符的数量是非常多的,各种各样的我们需要的“标准”即通过这些元字符来构成。其它的元字符符号及其作用如下:
符号 | 作用 | 示例 | 示例解释 |
---|---|---|---|
. | 匹配一个任意字符 | b.t | 可以匹配bat、but、b#t、b1t等等 |
\w | 匹配一个字母、数字或者下划线 | b\wt | 可以匹配bat、b1t、b_t等等 |
\s | 匹配空白字符(包括\r、\n、\t等) | love\syou | 可以匹配到love you |
\d | 匹配任意一个数字 | \d\d | 可以匹配00、19、53等等 |
\b | 匹配一个位置,代表单词的开头或者结尾 | \bthe\b | 只会匹配到the,而像them中包含的the则匹配不到 |
^ | 匹配字符串的开始 | ^the | 匹配以the开头的字符串 |
$ | 匹配字符串的结尾 | the$ | 匹配以the结尾的字符串 |
\W | 匹配非字母、数字、下划线 | 和\w相反 | |
\S | 匹配非空白字符 | 和\s相反 | |
\D | 匹配非数字 | 和\d相反 | |
\B | 匹配非单词边界 | ||
[] | 匹配来自字符集的任意一个字符 | [abcde] | 可以匹配到a、b、c、d、e |
[^] | 匹配不在字符集中的任意一个字符 | 和[]相反 | |
* | 匹配0次或多次 | ||
+ | 匹配1次或多次 | ||
? | 匹配0次或1次 | ||
{N} | 匹配N次 | ||
{M,} | 匹配至少M次 | ||
{M,N} | 匹配M次-N次 | ||
| | 分支 | a|b | 可以匹配a或b |
(?#) | 注释 | ||
(exp) | 匹配exp并捕获到自动命名的组中 | ||
(? |
匹配exp并捕获到名为name的组中 | ||
(?:exp) | 匹配exp但是不捕获匹配的文本 | ||
(?=exp) | 匹配exp前面的位置 | \b\w+(?=ing) | 可以匹配I‘m dancing中的danc |
(?<=exp) | 匹配exp后面的位置 | (?<=\bdanc)\w+\b | 可以匹配I love dancing and reading中的第一个ing |
(?!exp) | 匹配后面不是exp的位置 | ||
(? | 匹配前面不是exp的位置 | ||
(*?) | 重复任意次,但尽可能少重复 | a.*b和a.*?b | 将正则表达式应用于aabab,前者会匹配整个字符串,后者会匹配到aab和ab两个字符串 |
+? | 重复1次或多次,但尽可能少重复 | ||
?? | 重复0次或1次,但尽可能少重复 | ||
{M,N}? | 重复M到N次,但尽可能少重复 | ||
{M,}? | 重复M次以上,但尽可能少重复 |
分支条件是什么呢?在正则表达式中,分支条件指的是有几种规则,如果满足任意一种规则都应该被匹配,使用的时候用|
隔开,简单举个例子:
假如我们想匹配以5或7开头的5位数字,那么你可能这么写:
r'[57]\d{4}'
但实际上你利用分支条件的话可以这么写:
r'5\d{4}|7\d{4}'
看到这儿你可能会想,这不是更麻烦了吗,有什么用呀还不如第一种写法,嗯,也许针对这个例子来讲确实第一种写法好点,但是在有些情况下还是挺好的。
比如:
r'0\d{2}-\d{8}|0\d{3}-\d{7}'
它能同时匹配到012-12345678和0123-1234567,你难道可以在不使用分支条件的情况下用一个正则表达式同时匹配到这两个字符串吗,显然是不能的。
在我们前边了解到的元字符中,有对字符进行匹配次数限制的,如加一个?
则代表匹配0次或1次,而这个问号是对某单个字符进行匹配次数限制的,如:
r'\d\d?'
即这个?
只能限制后一个\d
,对第一个\d
并没有效果,如果想要修饰多个字符的话,我们可以用小括号来指定子表达式(也叫做分组),然后就可以指定这个子表达式的重复次数了,如:
r'(\d{1,3}.){3}\d{1,3}'
它匹配的应该是类似这样的:123.45.6.78
在我们使用小括号指定一个子表达式后,匹配到的这个子表达式的文本内容是可以被我们引用做进一步处理的。在默认情况下,我们指定的每一个子表达式都会拥有一个组号,我们通过引用这个组号来实现对匹配到的文本内容进行引用。
那么后向引用是什么有什么用呢?后向引用可以用来重复搜索之前某个分组匹配的文本。举个例子:
你可以这么写python表达式(后文会讲到python表达式使用):
p = re.compile(r'(\w+)\d{3}\1')
print(p.findall('gold123gold,sun567sun'))
#输出:
['gold', 'sun']
可以看到输出结果是:['goldsun','sun']
,我们看下正则表达式:r'(\w+)\d{3}\1'
,它的意思是先分一个组,组的编号就是1
,组的内容就是\w+
,也就是匹配1个或多个字母数字下划线,然后接上三个数字,最后的\1
指的是\w+中已经匹配到的文本内容
,也就是这里是和1分组的内容是完全一样的,所以就刚好能匹配’gold123gold’和’sun567sun’。
通常来讲后向引用就是这么用的,有些重复的内容不必再写,直接引用之前的组就可以,简单来讲默认的组号分配就是:从左向右,以分组的左括号为标志,第一个出现的分组组号为1,第二个为2,以此类推。
你也可以自己指定子表达式的组名而不采用默认分配,如要指定一个子表达式的组名,可以使用这样的语法:
(?
\w+)(或者把尖括号换成'也行)
这样就把\w+的组名指定为groupname了,要反向引用这个分组捕获的内容,可以使用\k
。
在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 | 多行匹配标记 |
下面将通过一些例子来介绍函数的使用
首先,上面写到的这些函数中都有pattern
参数,这个参数是正则表达式的式子,而在使用中也可以利用正则表达式对象来替代这些函数的使用,特别是在一些表达式需要重复使用的时候,创建出正则表达式的对象并利用是更加明智的选择。示例如下:
#我们需要验证某一字符串是否是我们想要的,
#如果是就删除(有语病不要在意哈哈哈)
#要求:字符串必须是5-11位数字构成且首位不能是0
import re
numlist = ['123456','0645789','564897954']
#第一种方法,直接编写正则表达式验证
for i in numlist:
if re.match(r'[1-9]\d{4,10}',i):
numlist.remove(i)
print(numlist)
#第二种方法,创建正则表达式对象验证
c = re.compile(r'[1-9]\d{4,10}')
for i in numlist:
if c.match(i):
numlist.remove(i)
print(numlist)
这两种方法输出结果都是一样的:['0645789']
,通过这个例子便能清晰了解创建正则表达式对象和直接书写之间的差别。
实际上需要重点说明的是每一个函数的可能大部分人第一次不会注意到的作用
match
方法用来匹配字符串,注意是从整个字符串中匹配满足要求的部分。search
方法搜索符合正则表达式模式的第一个匹配对象,有的话就返回第一个对象,没有就是Nonefullmatch
方法要求要匹配的字符串满足要求,即这个字符串本身完全满足要求,而不能是字符串中包含满足要求的部分。findall
方法非常常用,就是从字符串中返回所有满足匹配要求的字符串列表。注意返回是列表对象,可以随时拿来做其他处理。例子:从一段文字中提取出相关国内手机号码(肯定不是完美匹配所有,因为咱也不知道手机号码都有哪些号码段哈哈)
import re
def main():
# 创建正则表达式对象 使用了前瞻和回顾来保证手机号前后不应该出现数字
pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)')
sentence = '''
重要的事情说8130123456789遍,我的手机号是13512346789这个靓号,
不是15600998765,也是110或119,王大锤的手机号才是15600998765。
'''
# 查找所有匹配并保存到一个列表中
mylist = re.findall(pattern, sentence)
print(mylist)
print('--------华丽的分隔线--------')
# 通过迭代器取出匹配对象并获得匹配的内容
for temp in pattern.finditer(sentence):
print(temp.group())
print('--------华丽的分隔线--------')
# 通过search函数指定搜索位置找出所有匹配
m = pattern.search(sentence)
while m:
print(m.group())
m = pattern.search(sentence, m.end())
if __name__ == '__main__':
main()
#输出
['13512346789', '15600998765', '15600998765']
--------华丽的分隔线--------
13512346789
15600998765
15600998765
--------华丽的分隔线--------
13512346789
15600998765
15600998765
以上就是本篇文章的全部内容了,如果你感觉有些地方没有懂那么可以提出问题大家一起解答,或者你也可以去看一篇很好很有名的博客《正则表达式30分钟入门教程》,最后如果你觉得本篇文章对你有用的话不妨点个赞,谢谢啦,让我们一起努力、一起进步!