正则表达式(一)
0、注意事项
1、转义字符是 \ 不是常用的 /
2、^在中括号里面和外面含义不同:在外时,就表示以它后边跟的一个字符开头,如^7[0-9]表示匹配开头是7的,且第二位是任一数字的字符串;如果在中括号里面,表示除了(反选)这个字符之外的任意字符(包括数字,特殊字符),如[^abc]表示匹配除abc之外的其他任一字符。
1、参考
https://www.cnblogs.com/whatisfantasy/p/6014523.html
2、说明
正则表达式是一种用来匹配字符串的强有力的武器。其设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它‘匹配’了,否则,该字符串就是不合法的。
比如说,我们用来判断一个字符串是否是合法Email的方法是:
1、创建一个匹配Email的正则表达式;
2、用该正则表达式去匹配用户的输入来判断是否合法。
因为正则表达式也是用字符串表示的,所以我们要先了解如何用字符描述字符。
3、规则
全规则
1. 任意单字符表示匹配任意对应的字符,如a匹配a,7匹配7,-匹配-。
2. [ ]代表匹配中括号中其中任一个字符,如[abc]匹配a或b或c。
3.-在中括号里面和外面含义不同:在外时,就匹配它自己;如果在中括号内,表示范围,例如[a-z]表示匹配26个小写字母中的任一个;[a-zA-Z]匹配大小写共52个字母中任一个;[0-9]匹配十个数字中任一个。
4.^在中括号里面和外面含义不同:在外时,就表示以它后边跟的一个字符开头,如^7[0-9]表示匹配开头是7的,且第二位是任一数字的字符串;如果在中括号里面,表示除了(反选)这个字符之外的任意字符(包括数字,特殊字符),如[^abc]表示匹配除abc之外的其他任一字符。
5. .表示匹配任意的字符。
6. \d表示数字。
7. \D表示非数字。
8. \s表示由空字符组成,[ \t\n\r\x\f ]。
9. \S表示由非空字符组成,[^\s]。
10. \w表示字母、数字、下划线,[a-zA-Z0-9_]。
11. \W表示不是由字母、数字、下划线组成。
12. ? 0次或1次。
13. + 1次或多次。
14. * 0、1或多次。
15. {n} n次。
16. {n,m} n~m次。
17. {n,} n次或n次以上。
18. XY表示X后面跟着Y,这里X和Y分别是正则表达式的一部分。
19. X|Y表示X或Y,比如"food|f"匹配的是foo(d或f),而"(food)|f"匹配的是food或f。
20. (X)子表达式,将X看做是一个整体;
各个规则更细致说明,看后边各具体项
3.1、如果直接给出字符,那就是精确匹配;
3.2、正则字符
\d:一个数字
\D:非数字
\w:一个字母、数字、下划线
\W:非字母、数字、下划线
\s:一个空格(包含Tab)
\S:非空白字符
\b:匹配一个单词边界(开头、结尾),即匹配内容刚好在边界上,这个边界可以是空格和特殊字符
\b在前边,则指明是前边界,\b在后边,则指明是后边界
3.2.1、例子(以下例子都是python语法)
①'00\d'可以匹配'007'但不能匹配'00A'
②'\d\d\d'——'010'
③'\w\w\d'——'py3'
\b的例子
ret=[] re1=re.compile(r'\babc')#abc在开头作为边界 re2=re.compile(r'\bI') #开头的I作为边界 re3=re.compile(r'abc\b')#abc在结尾作为边界 ret.append(re1.findall('asdas abc 1231231')) ret.append(re1.findall('asdas*abc 1231231')) ret.append(re2.findall('I try to Image')) ret.append(re3.findall('asdasabc*1231231')) for i in range(len(ret)): print (ret[i]) ['abc'] ['abc'] ['I', 'I'] ['abc']
3.3、. :匹配任意字符,除了\n
'py.'——'pyc'、'py2'、'py!'……
这里很容易出错,详见补充④
3.4、要匹配变长的字符,在正则表达式中:
*:任意个字符(包含0个)等价于 {0,}
+:至少一个字符 {1,}
?:0或1个 {0,1}
{n}:n个字符
{n,m}:n~m个字符
3.4.1、例子
\d{3]\s+\d{3,8}
解读
\d{3}:匹配3个数字,如'010'
\s+:至少一个空格
\d{3,8}:3-8个数字,如'1234567'
综合起来,上面的正则表达式可以匹配任意个空格隔开的带区号的电话号码。
如果要匹配'010-12345'呢?由于'-'是特殊字符,在正则表达式中,需要用转义符号'\'转义,所以写法应该是\d{3}\-\d{3,8}
但是仍无法匹配'010 - 12345',因为带有空格,所以我们需要更复杂的匹配方式。
3.5、[ ]:表示范围
[0-9a-zA-Z\_]:匹配一个数字、字母或下划线;
[0-9a-zA-Z\_]+:匹配 至少由一个数字、字母或下划线组成的字符串,比如'a100','0_Z','Py3000'……
[a-zA-Z\_][0-9a-zA-Z\_]*:匹配 由字母或下划线开头,后接任一个由数字、字母、下划线组成的字符串,也就是Python合法变量
[a-zA-Z\_][0-9a-zA-Z\_]{0,19}:匹配 由字母或者下划线开头,后接0~19个数字、字母、下划线组成的字符,更精确地限制了字符串的长度为1-20个字符
[0-3,5-8]:匹配数字(0,1,2,3,5,6,7,8)
注意:
1、元字符在[ ]中不再具有特殊意义而是变成了普通字符,但是/d /w /s除外
2、[a-zA-Z]效果等同于[a-z , A-Z]
[1-35-7]效果等同于[1-3,5-7]
3、[ ]中的^表示反向匹配
[^0-9]:匹配除数字外的字符,相当于\D
3.6、A|B:匹配A或B
(P|p)ython ——'Python'或'python'
3.7、^:字符串开头
^\d:以数字开头
3.8、$:字符串的结尾
\d$:必须以数字结束
^与$的结合可以构成整行匹配:
^py可以匹配'python'
^py$只能匹配 'py'
4、re模块
Python提供了re模块,包含所有正则表达式的功能。由于Python字符串也用'\'表转义,所以可以写字符串时加上r前缀,就不用考虑转义的问题了。(暂时没发现加与不加r前缀的区别,所以此处暂存疑!)(回答是:必须转义的字符必须加,剩下的特殊字符如果无歧义可加可不加)
print('ABC\\-DEF') ABC\-DEF print(r'ABC\-DEF') ABC\-DEF
4.1、match():正则表达式匹配
import re print(re.match(r'\d{3}\-\d{3,8}$','010-12345')) print(re.match(r'\d{3}\-\d{3,8}$','010 123456')) <re.Match object; span=(0, 9), match='010-12345'> None
匹配成功返回一个Match对象,否则返回None。常用的判断方法是:
test='用户字符串' if re.match('正则表达式',test): print('OK!') else: print('Failed!')
4.2、切分字符串:split()
用正则表达式切分字符串比用固定的字符更灵活。
正常的切分字符串的代码:
'a b c'.split(' ') ['a', 'b', '', '', 'c']
无法识别连续的空格,我们用正则表达式尝试:
re.split(r'\s+','a b c') ['a', 'b', 'c']
无论多少个空格都可以正常分割。加入 , 再进行实验
re.split(r'[\s\,]+','a, ,b, c') ['a', 'b', 'c']
[\s\,]+:至少有一个空格或者逗号
再加入分号 ; 进行尝试:
re.split(r'[\s\,\;]+','a, ; , ; b ,,,; ;c') ['a', 'b', 'c']
[\s\,\;]+
如果用户输入了一组标签,可以用正则表达式把不规则的输出转化为正确的数组。
从本例可以看出,如果想把某些不需要的字符从字符串中剔除,只保留剩下的字符,通过构建正则表达式[]+,并将不需要的字符放入其中即可。
4.3、分组:group()
除了简单判断是否匹配之外,正则表达式还可以用来提取子串。用( )表示的就是要提取的分组(Group)。
比如:
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
m=re.match(r'^(\d{3})-(\d{3,8})$','010-12345') m <re.Match object; span=(0, 9), match='010-12345'> m.group(0) '010-12345' m.group(1) '010' m.group(2) '12345' m.groups() ('010', '12345')
如果正则表达式中定义了组(正则表达式中的一个()对应一个组),就可以在Match对象上用group()方法提取出子串来。
group(0)永远是原始字符串,group(1)、group(2)、……表示第1、2、……个子串,此外groups()是一个包含了所有子串的tuple。
另外有一个识别时间的例子:
t='19:05:30' m=re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$',t) m.groups() ('19', '05', '30')
这个正则表达式可以直接识别合法的时间。但有的时候,正则表达式也无法完全验证,比如日期的识别:
'^(0[0-9]|1[0-2]|[0-9])-(0[0-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$'
对于2-30,4-31这样的非法日期,用正则也识别不了,或者说写出来很困难,这时候就需要程序配合识别了。
4.4、贪婪匹配
正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后边的0:
re.match(r'^(\d+)(0*)$','102300').groups() ('102300', '')
由于\d+采用贪婪匹配,直接把后边的0全部匹配了,结果0*只能匹配空字符串了。
必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后边的0匹配出来,加个?就可以让\d+采用非贪婪匹配:
re.match(r'^(\d+?)(0*)$','102300').groups() ('1023', '00')
4.5、预编译:compile()
当我们在Python中使用正则表达式时,re模块内部会干两件事情:
1、编译正则表达式,如果正则表达式的字符串本身不合法,会报错
2、用编译后的正则表达式去匹配字符串
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:
importre #编译 re_telephone=re.compile(r'^(\d{3})-(\d{3,8})$') #使用 re_telephone.match('010-12345').groups() ('010', '12345') re_telephone.match('010-8086').groups() ('010', '8086')
编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以在调用对应的方法时不用给出正则字符串(如上文中直接调用该对象的match方法)。
补充:
①单一类型的字符没必要用[ ],只有当要求匹配不是同一类的多种字符时,才用[ ]
如[\w]+完全可以写成\w+
而包含逗号,分号时,就要用[ ],如[\w\,\;]+
②注意转义符号是反斜杠\,而不是斜杠/,这一个小问题经常导致匹配错误又检查不出来!!
③分组总是保存在group(1),group(2)……中,即使筛选时就一个括号(),也要通过group(1)
★④提取网址、邮箱等包含 . 的字符串时,由于 . 表示任何字符,所以要想不能匹配到别的网站,必须对.进行转义,即写成\.的形式
比如:163邮箱的提取,要求@前有4-20位
r'^[\w]{4,20}@163.com$' #错误 可能匹配到类似XXX@1633com r'^[\w]{4,20}@163\.com$'#正确
☆重要!!!!!!!
⑤正则串中有多少个括号(),匹配结果的groups中就有几个结果。(即,当你想输出或查看匹配项时,你的正则表达式中必须有括号()!)
这样,我们就知道了( )的两个用途:①分组group;②(P|p)
两个邮箱匹配的例子
①验证Email地址的正则表达式。
someone@gmail.com √
bill.gates@microsoft.com√
bob#example.com ×
mr-bob@example.com ×
def is_valid_email(addr): m=re.compile(r'^[\w\.]+@\w+\.com$') if m.match(addr): return True else: return False
②可以提取出带名字的Email地址
<Tom Paris> tom@voyager.org => Tom Paris
bob@example.com => bob
def name_of_email(addr): m=re.compile(r'^<?([\w\s]+)>?[\w\s]*@\w+\.\w+$') #或者 #m=re.compile(r'^<?(\w+\s*\w*)>?\s*\w*@\w+.\w+$') n=m.match(addr) return n.group(1)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性