用Python学习正则表达式
转自:http://daydayup.is-programmer.com/posts/1200.html
用Python学习正则表达式
-
- 一般字符 []表示的字符类 {}表示重复 ^和$表示行头和行尾 ()表示分组 正则表达式的优先权顺序 小结
- 一个好的网址
正则表达式
正则表达式是一种字符串匹配模式。
一般字符
对于绝大多数的字符来说,它们都只是简单地匹配自己。如
>>> p=re.compile('hi') # 匹配 hi >>> m=p.search('very,high') # >>> m.span() # 找到了位置 (5, 7) >>>
在正则表达式中,如下的字符是具有特殊含义的
. ^ $ * + ? { [ ] \ | ( )
它们的含义,我们会慢慢地说。
- \
- 象其它大部分语言一样,'\'表示转义。为了在你的字符串中匹配如上的特殊字符,你就需要用到它。还有就是一些常见的定义,如'\r','\n','\f','\t','\b'等。如
>>> p=re.compile(r'\.e') #找字符'.'后面紧跟字符'e' >>> m=p.search('a.exe') >>> m.span() (1, 3) >>>
- .
- 字符.用于匹配任意的非换行字符,在windows下为'\n',在unix下为'\r\n'。在Python中,如果你在构造表达式时指定参数DOTALL的话,字符也可以匹配换行。
>>> p=re.compile(r'.') #找任意非\n的字符 >>> p.search('abc').span() (0, 1) >>> p.search('\ncba').span() (1, 2) >>> p=re.compile (r'.',re.DOTALL) #找任意字符,包括\n在内 >>> p.search('\ncba').span() (0, 1) >>>
- |
- 字符|表示选择,匹配它两边的任意一种。
>>> p=re.compile(r'abc|cba') #匹配 abc 或 cba >>> p.search('cabcd').span() #找到 abc (1, 4) >>> p.search('cbcbad').span() # 找到 cba (2, 5)>>>
[]表示的字符类
如果你想匹配几个字符中的任意一个,那么就要用到字符类了。一个字符类是由[和]表示的,表示其中的任意一个。如
>>> p=re.compile(r'[aeiou.^]') #找aeiou,字符.或字符^中的任意一个 >>> m=p.search('b.exe') #找到了字符. >>> m.span() (1, 2) >>>
在一个字符类中,只有字符^、-、]和\有特殊含义。字符\仍然表示转义,字符-可以定义字符范围,字符^放在前面,表示非,字符]表示字符类的结束。如
>>> p=re.compile(r'[-a-c]') #匹配字符-和字符a到c之间的字母,即a,b,c和-。 ... #第1个字符-就表示字符-,第2个表示范围 >>> m=p.search('d-a.exe') #找到- >>> m.span () (1, 2) >>> m=p.search('b-a.exe') #找到b >>> m.span() (0, 1) >>> p=re.compile(r'[^-a-c]') #找非-,a,b,c的字符 >>> m=p.search('b-a.exe') #找到字符.>>> m.span() (3, 4) >>> p=re.compile(r'[-a-c^]') #^没有在首位,就只表示字符^ >>> m=p.search('def^-') #匹配字符^ >>> m.span() (3, 4) >>> p=re.compile(r'[[-a-c]') #第2个字符[只是表示字符[>>> m=p.search('a[') #找到[ >>> m.span() (0, 1) >>> p=re.compile(r'[a-c]]') #第1个]已经表示类的结束,第2个表示字符] ... #表示要匹配字符a,b或c后接字符] >>> m= p.search(']a') #匹配失败 >>> m.span()Traceback (most recent call last):File "<pyshell#37>", line 1, in <module>m.span() AttributeError: 'NoneType' object has no attribute 'span'>>> m=p.search('a]') #成功 >>> m.span() (0, 2) >>> p=re.compile(r'[a-c\]]') #要表示字符],需要用转义符\ >>> m=p.search(']a') #匹配到字符] >>> m.span() (0, 1) >>>
有若干常用的字符类的表达
转义 | 含义 | 可能的等价表达 |
\d | 任意数字 | [0-9] |
\w | 任意单词字符,包括下划线_,数字和字母 | [a-zA-Z0-9_] |
\s | 空白字符 | [ \t\r\f\n\v] |
\D | 非数字 | [^\d] |
\W | 非单词字符 | [^\w] |
\S | 非空白 | [^\s] |
注意的就是,对于Unicode的实现,会不一样。如\w会包含汉字字符在内。
{}表示重复
在{和}之间的数,用于指定重复的次数
{n} | 重复n次 |
{n,} | 重复n次或n次以上 |
{n,m} | 重复n到m次 |
如
>>> p=re.compile(r'ab{2}c'); #匹配abbc >>> p=re.compile(r'ab{2,}c'); #匹配abbc,abbbbbbc >>> p=re.compile(r'ab{2,4}c'); #匹配abbc,abbbc,abbbbc>>>
除了用{}表示重复外,还有几个字符也是这种含义
* | 重复0次或更多次,与{0,}等价 |
? | 重复0次或1次,与{0,1}等价 |
+ | 重复1次或更多次,与{1,}等价 |
>>> p=re.compile(r'windows\d+'); #匹配windows后跟至少1个数字 >>> p=re.compile(r'13\d{9}'); #匹配13后跟9个数字 >>>
懒惰模式
在正则表达式的匹配中,采用的尽可能多的匹配原则,即贪婪模式(Greeding),如
>>> p=re.compile(r'a.*c'); >>> m=p.search(r'abcbca'); #匹配尽可能多的情况,即abcbc,而不是abc >>> m.span() (0, 5) >>>
如果说想要采用尽可能少的匹配的话,可以使用懒惰模式(Lazy)。在表达重复的符号后,加上字符?即可,如+?,*?,{n,}?等。
* ? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
>>> p=re.compile(r'a.*?c'); #指定Lazy模式 >>> m=p.search(r'abcbca'); #匹配到abc就结束 >>> m.group() 'abc' >>>
^和$表示行头和行尾
字符^表示在一行的开始位置,字符$则表示行的结尾位置,它们不代表任何字符。在Python中,需指定参数re.M或re.MULTILINE,才会表示行头和行尾;否则则表示字符串的开关和结尾。
>>> p=re.compile(r'^From:',re.M) #匹配以From:开始的行 >>> p.search('hello,\nFrom: rui').span() #找到 (7, 12) >>> p.search('hello, From: rui').span() #失败Traceback (most recent call last):File "<pyshell#15>", line 1, in <module>p.search('hello, From: rui').span() AttributeError: 'NoneType' object has no attribute 'span'>>> p=re.compile(r'^From:') #没加参数,则表示要求字符串以From:开头 >>> p.search('hello,\nFrom: rui').span() #失败Traceback (most recent call last):File "<pyshell#17>", line 1, in <module>p.search('hello,\nFrom: rui').span() AttributeError: 'NoneType' object has no attribute 'span' >>> p.search('From: rui').span() #成功 (0, 5) >>>
除了用^和$表示位置外,还有
\b | 匹配一个单词边界,也就是指单词和非单词字符间的位置。 |
\B | [^\b] |
\A | 匹配字符串的开头 |
\Z | 匹配字符串的结尾 |
如:
>>> p=re.compile(r'ba\b') # 找以ba为结尾的单词 >>> m=p.search('cba') # 找到,后面可以是结束 >>> m.span() # \b本身不占用字符位 (1, 3) >>> m= p.search('cba ') # 可以是空格 >>> m.span() (1, 3) >>> m=p.search('cba-') # 可以是非单词字符 >>> m.span() (1, 3) >>> m=p.search('cbac') # 失败>>> m.span()Traceback (most recent call last):File "<pyshell#55>", line 1, in <module>m.span() AttributeError: 'NoneType' object has no attribute 'span'>>>
()表示分组
前面讲了对单个字符的重复,如果你想重复一个子串的话,就可以采用分组的方法
>>> p=re.compile(r'(ab)+') #匹配一个以上的ab >>> p.search('acabababc').span()#成功 (2, 8) >>> p=re.compile(r'(ab|bc)+') #匹配一个以上的ab或bc >>> p.search('acabbcababc').span() (2, 10) >>>
分组除了当作了表达式外,还有个重要的功能就是记录。(和)中匹配的内容会被记录下来,以备以后引用。在Python中,MatchObject的方法 group(),start(),end(),span()可以传入一个整形参数来得到这些记录。记录以0开始计数,并且0表示整个匹配了的表达式,是这些方法的缺省参数。分组从左到右从1开始计数。
>>> p=re.compile(r'(a(b)c)d') >>> m=p.search('abcd') >>> m.group() #就是缺省参数0 'abcd' >>> m.group(0) 'abcd'>>> m.group(1) 'abc' >>> m.group(2) 'b' >>> m.group(3) #超出范围Traceback (most recent call last):File "<pyshell#33>", line 1, in <module>m.group(3) IndexError: no such group >>> m.groups() #列出所有的分组 ('abc', 'b') >>>
在正则表达式中,还可以对分组做出向后引用,是用'\数字'来表示。即\1表示第1个分组,\2表示第2个,等。
>>> p = re.compile(r'(\b\w+)\s+\1') # 匹配两个同样的单词连着出现, ... # 即找一个单词,并且在若干个空格后,还是同样的单词 ... # \1 表示的是前面的分组(\b\w+)匹配的同样的内容 >>> p.search('Paris in the the spring').groups() ('the',) >>> p.search('Paris in the the spring').group() #找到 'the the' >>>
非记录分组和命名分组
若一个表达式很复杂,有很多的分组,而每个分组都要记录的话就会很困难。有两种相应的方法来处理这个问题,不过属于正则表达式的扩展。
在Perl中,它定义了一种正则表达式的扩展,就是在括号后紧跟一个问题,即'(?...)'。而紧跟在问号'?'后的字符则表达了扩展的含义,如(?:foo)表示非记录型的分组含foo,而(?=foo)则表示另一种含义(表示前面有foo)。
除了实现Perl的这些扩展外,Python还有自己的扩展。若在'?'后紧跟的是P的话,则表示是Python的扩展。如(?P< name>...)定义了一个命名分组,而(?P=name)则表示的对前面的向后引用(与\1等的功能一样)。若Perl中引入了同样功能的符号,Python会同样引入,同时保留P字符功能。
- (?P<name>...)和(?P=name)
- (?P<name>...)用来定义一个命名分组,它可以通过MatchObject的方法group('name')得到,同时,在表达式中,也可以用(?P=name)来表示对它的引用。
>>> p = re.compile(r'(?P<word>\b\w+\b).*(?P=word)') #注意命名分组及对它的引用 >>> m = p.search( '(((( Lots of punctuation aLots)))' ) >>> m.group() 'Lots of punctuation aLots'>>> m.group('word') #在方法中引用 'Lots' >>> m.span('word') (5, 9) >>>
在imaplib模块中,有个读取INTERNALDATA的例子
InternalDate = re.compile(r'INTERNALDATE "'r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'r'(?P<year>[0-9][0-9][0-9][0-9])'r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'r'"')
显然,用InternalData.group('zonen')比InternalData.group(9)要好得多。
- (?:...)
- 表示非记录分组。即这个分组不会被记录下来。
>>> m = re.match("([abc])+", "abc") >>> m.groups() ('c',) >>> m = re.match("(?:[abc])+", "abc") #分组不被记录 >>> m.groups() ()
- (?=...)
- 称为零宽度正预测先行断言。它断言自身出现的位置的后面能匹配表达式...,但它不包括这些表达式。就像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(断言),因此它们也被称为零宽断言。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分)。
>>> p=re.compile(r'Rui (?=Zhang)') #匹配Rui 后面跟Zhang时的情况,不包括Zhang在内 >>> p.search('hi, Rui Zhang').group() #成功,匹配的内容不含Zhang 'Rui ' >>> p.search ('hi, Rui Zhang').groups() #分组中没有被记录的部分 () >>> p.search('hi, Rui Zhou').group() #Rui 后面跟的是Zhou,失败Traceback (most recent call last):File "<pyshell#8>", line 1, in <module>p.search('hi, Rui Zhou').group() AttributeError: 'NoneType' object has no attribute 'group' >>>
- (?<= ...)
- 与前面类似,也是用于指定位置,不作实际匹配,叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式...。比如(?<= \bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分)
>>> p=re.compile(r'(?<=Rui )Zhang') #匹配前面是Rui 的字符Zhang >>> p.search('hi, Rui Zhang').group() #成功 'Zhang' >>> p.search('hi, Shan Zhang').group() #失败Traceback (most recent call last):File "<pyshell#14>", line 1, in <module>p.search('hi, Shan Zhang').group() AttributeError: 'NoneType' object has no attribute 'group'>>>
- (?!...)
- 零宽度负预测先行断言。断言此位置的后面不能匹配表达式...。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。
>>> p=re.compile(r'Rui (?!Zhang)') #匹配Rui 后面不是Zhang的情况 >>> p.search('hi, Rui Zhang').group() #失败Traceback (most recent call last):File "<pyshell#10>", line 1, in <module>p.search('hi, Rui Zhang').group() AttributeError: 'NoneType' object has no attribute 'group' >>> p.search('hi, Rui Zhou').group() #成功 'Rui ' >>>
- (?<!...)
- 零宽度负回顾后发断言。断言此位置的前面不能匹配表达式...。(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。
>>> p=re.compile(r'(?<!Rui )Zhang') #匹配前面不是Rui 的字符Zhang >>> p.search('hi, Rui Zhang').group() #失败Traceback (most recent call last):File "<pyshell#16>", line 1, in <module>p.search('hi, Rui Zhang').group() AttributeError: 'NoneType' object has no attribute 'group' >>> p.search('hi, Shan Zhang').group() #成功 'Zhang' >>>
- (?#...)
- 表示注释,其中的内容直接被忽略
>>> p=re.compile(r'Rui (?#Zhang)') #与'Rui '是等价的 >>> p.search('Rui Zhang').groups() () >>> p.search('Rui Zhang').group() 'Rui '>>>
- (?iLmsux)
- 在"i", "L", "m", "s", "u", "x"中的一个或多个,它不匹配任何字串,而是表示对应(re.I, re.L, re.M, re.S, re.U, re.X)的6种选项。
>>> p=re.compile(r'(?m)^From:') #与使用re.M选项是一样的 >>> p.search('hi,\nFrom: rui').span() (4, 9) >>>
- (?(id/name)yes-pattern|no-pattern)
- 这是Python 2.4后加入的特性。表示若分组id或name存在的话,就匹配yes-pattern,否则就匹配no-pattern。|no-pattern是可选的,若没有的话,就表示忽略它。如(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)就是个简单的E-Mail地址匹配表达式,它可以匹配'< user@host.com>'或' user@host.com'但不能匹配'< user@host.com'。其中最后一个分组表达(?(1)>)就表示若分组(1)存在(这里是指(<),即是否有符号<),则要求匹配表达式>,否则就忽略它。
>>> p=re.compile(r'(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)') >>> m=p.search('<xxx@gmail.com.cn>') >>> m.group () '<xxx@gmail.com.cn>' >>> m.groups() ('<', 'xxx@gmail.com.cn') >>> m=p.search('< xxx@gmail.com') >>> m.group() 'xxx@gmail.com' >>> m.groups() (None, 'xxx@gmail.com') >>> m=p.search('xx@gmail.com') >>> m.group() 'xx@gmail.com' >>> m.groups() (None, ' xx@gmail.com') >>>
正则表达式的优先权顺序
在构造正则表达式之后,就可以象数学表达式一样来求值,也就是说,可以从左至右并按照一个优先权顺序来求值。
下表从最高优先级到最低优先级列出各种正则表达式操作符的优先权顺序:
操作符 | 描述 |
\ | 转义符 |
(), (?:), (?=), [] | 圆括号和方括号 |
* , +, ?, {n}, {n,}, {n,m}
|
限定符 |
^, $, \anymetacharacter | 位置和顺序 |
|
|
"或"操作 |
小结
- "."
- (点.)代表任何非回车换行字符。当DOTALL选项指定时候,可以匹配包括回车在内的任意字符。
- "^"和"$"
- 表示字串的头和尾。若指定选项MULTILINE,还表示一行的头和尾
-
"
*
" - 表示重复0或更多个
- "+"
- 表示重复1个以上
- "?"
- 表示重复0或1个
-
*
?, +?, ?? -
是"
*
","+"和"?"的懒惰匹配,即尽可能少的匹配 - {m}
- 重复m个
- {m,n}
- 重复n到m个
- {m,n}?
- {m,n}的懒惰匹配
- "\"
- 转义字符。
- []
- 字符集。匹配[]中指定的任何一个。可以用"-"来指定一个范围,也可以用[^...]来表示不是其中的字符
- "|"
- A|B,其中A和B可以是表达式,表示匹配A或B中的任意一个
- (...)
- 分组
- (?...)
- 正则表达式的扩展标识
- (?iLmsux)
- (取字符"i", "L", "m", "s", "u", "x"中的1个或多个)不匹配任何字串,仅仅用于设置选项。它们分别对应于(re.I, re.L, re.M, re.S, re.U, re.X),一般放在表达式的第一位
- (?:...)
- 非记录分组
- (?P<name>...)
- 命名分组
- (?P=name)
- 取出命名的分组
- (?#...)
- 注释
- (?=...)
- 先行断言。断言此位置的后面能匹配表达式...。
- (?!...)
- 负先行断言。断言此位置的后面不能匹配表达式...。
- (?<=...)
- 后发断言。断言此位置的前面能匹配表达式...。
- (?<!...)
- 负后发断言。断言此位置的前面不能匹配表达式...。
- (?(id/name)yes-pattern|no-pattern)
- 若分组id若name已经匹配,则使用yes-pattern,否则用no-pattern。|no-pattern可选。
一个好的网址
正则表达式30分钟入门教程
Regular Expression HOWTO
--
6G免费网络U盘: http://www.orbitfiles.com/signup/rleon
Python学习笔记 - 正则表达式 (by Rui Zhang)
用Python学习正则表达式相关推荐
- Python学习——正则表达式与re模块实现字符串计算器
学习了python的正则表达式后,一片懵逼,不知道干啥用的,也不知道咋用,只能放一些实例和正则表达式的规则以备后续查阅,希望在经过长时间的训练和使用后能对正则表达式有一个深刻的理解.什么是正则表达式呢 ...
- python学习——正则表达式
正则表达式的理解 通俗的理解就是在大量数据中,匹配自己想找到的内容,或者筛选合格的内容,过滤掉不合格的内容. 在python中,正则表达用到了re库,是pycharm自带的库,包含了众多方便使用正则表 ...
- python学习--正则表达式
正则表达式是一种用来匹配字符串的强有力的工具它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它"匹配"了,否则,该字符串就是不合法的. Py ...
- Python学习教程(Python学习路线):第12天—正则表达式
Python学习教程(Python学习路线):正则表达式相关知识 在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是 ...
- python正则匹配日期2019-03-11_都2019年了,正则表达式为啥还是这么难?这里的Python学习教程教你搞定!...
都9102年了,你还觉得正则表达式很难?难,确实是还难啊! 这里南瓜跟大家总结的最新Python学习教程,教你搞定它! 正则表达式语法 字符与字符类 特殊字符: .^$?+*{}| 以上特殊字符要想使 ...
- python 学习总结----正则表达式
python 学习总结----正则表达式 正则表达式应用场景- 特定规律字符串的查找,切割,替换 - 邮箱格式:URl,IP地址等的校验 - 爬虫项目中,特定内容的提取使用原则- 只要使用字符串等函数 ...
- Python学习:day21正则表达式
写在前面: 此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 传送门: day01基础知识 day02知识分类 day03 ...
- Python学习:day20正则表达式
写在前面: 此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 传送门: day01基础知识 day02知识分类 day03 ...
- 学习 正则表达式 js java c# python 通用
正则表达式 js java c# python 学习网站 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Globa ...
最新文章
- Hough变换原始形式-直线检测
- 关闭文件夹或打印机共享服务器,局域网共享打印机好用,但文件夹不能访问
- Java验证(javafx)
- java href_jnlp href属性中的url参数
- ChinaJoy 第二天,是谁独得万千宠爱?
- Java中try必须要结合catch吗_如何优雅的实现 try/catch 异常块?
- OpenGl文章 Android OpenGL ES 简明开发教程
- 关注电子商务网站开发-《网站中常用的Jquery插件》
- 一、CC2530简介
- 【统计学】三大相关系数之斯皮尔曼相关系数(spearman correlation coefficient)
- 希捷 服务器文件丢失 原因,移动硬盘数据丢失的原因有哪些?如何进行专业的数据恢复?...
- 北美票房:奥斯卡提名影片票房回春
- 彻底解决Android Studio Minimum supported Gradle version is X.Y.Z. Current version is x.y.z.问题
- logo设计的方法和技巧
- 河海大学计算机辅助,黄瑞
- 面试题:数组和链表的区别
- 【干货分享】微信与ERP支付互通 世界会怎样-解决方案推荐
- 计算机网络 构建Web内容的技术
- 985学校计算机实力排名2015,985大学名单及分档排名分析
- 如何配置Instantiable pallets
热门文章
- js测试代码运行时间
- 当下常见的企业文件管理工具都有哪些?
- HTTP与HTTPS区别
- MavenCentral发布指南
- [ElementUI] 日期选择器unlink-panels属性作用
- 苏格拉底对失恋者说的话
- Win10 SQL Server 2017安装教程
- 如何服务器上查找nginx网站目录下,查看nginx安装目录和版本的多种方法、命令详细介绍...
- 文件不以'%PDF—开始'之解决方法
- python3中urlopen_详解python3urllib中urlopen报错的解决方法