一、Python正则表达式re模块简介

正则表达式,是一门相对通用的语言。简单说就是:用一系列的规则语法,去匹配,查找,替换等操作字符串,以达到对应的目的;此套规则,就是所谓的正则表达式。各个语言都有各自正则表达式的内置模块,包括Linux系统中sed、awk也都是使用正则表达式。当然Python中也有对正则表达式的支持,对应的就是Python内置的re模块。

Python的re模块(Regular Expression,正则表达式)提供各种正则表达式的匹配操作,使用这一内嵌于Python的语言工具,尽管不能满足所有复杂的匹配情况,但足够在绝大多数情况下能够有效地实现对复杂字符串的分析并提取出相关信息。Python会将正则表达式转化为字节码,利用C语言的匹配引擎进行深度优先的匹配。

二、正则表达式(Regexp)

正则表达式是由普通字符(例如字符a到z)以及特殊字符(称为”元字符”)组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

  • 普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

  • 特殊字符

所谓特殊字符,就是一些有特殊含义的字符,如tes*t中的*,简单的说就是表示任何字符串的意思。如果要查找字符串中的*符号,则需要对*进行转义,即在其前加一个\,如tes\*t匹配tes*t。许多元字符要求在试图匹配它们时特别对待,若要匹配这些特殊字符,必须首先使字符”转义”,即,将反斜杠字符\放在它们前面。

  • 限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。常用有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 等等。

  • 定位符

定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。

下面表格列出了常用的正则表达式符号并给出了说明:

符号 说明 例子
.,.* “.”表示任意字符,除换行符’\n’外。如果说指定了DOTALL的标识,就表示包括新行在内的所有字符。而“.*”表示任意长度任意字符 ‘.est’ 可以匹配‘test’但不能匹配‘tooltest’,但‘.*est’可以匹配‘test’和‘tooltest’。
^ 匹配以某某字符串开头 ‘test’可以匹配‘test’和‘tooltest’,但‘^test$’只能匹配‘test’。
$ 匹配以某某字符串结尾 ‘test’可以匹配‘test’和‘testtool’,但‘test$’只能匹配‘test’。
*,+,? ‘*’表示后面可跟0个或多个字符,’+’表示后面可跟1个或多个字符,’?’表示后面可跟0个或多个字符 正则表达式’ab*’如果用于查找’abbbc’,将找到’abbb’。’ab?’将找到’ab’;而如果使用非贪婪的数量词’ab*?’,将找到’a’。
*?,+?,?? 在上面的结果中只取第一个 <*>会匹配'<H1>title</H1>’整个字符串(贪婪匹配),使用*?可以只找出<H1>(非贪婪匹配)。
{m} 对于前一个字符重复m次 a{3}匹配3个’a’,’aaa’可以匹配,但’aaba’无法匹配。
{m,n} 对于前一个字符重复m到n次 a{2,4}匹配2-4个a;a{2,}匹配2个以上a;a{,4}匹配4个以下a。
{m,n}? 对于前一个字符重复m到n次,并且取尽可能少的情况 在字符串’aaaaaa’中,a{2,4}会匹配4个a,但a{2,4}?只匹配2个a。
\ 对特殊字符进行转义,或者是指定特殊序列  
[] 表示一个字符集 [abc]会匹配字符a,b或者c,[a-z]匹配所有小写字母,[a-zA-Z0-9]匹配所有字母和数字,[^6]表示除了6以外的任意字符。注意[]一次只能匹配单个字符,一般配合[]*或[]{m,n}使用。
| 表示或者,只匹配其中一个表达式 A|B,如果A匹配了,则不再查找B,反之亦然。一般可以在[]或()中使用。
(pattern) 匹配括号中的任意正则表达式,并捕获其结果放到一个分组中 ([\d]*)会匹配任意数字,并把匹配到的结果放到当前分组中,默认此分组number为1。
(pattern)\<number> 使用\<number>可以引用编号为number的分组匹配到的字符串 ([\d]*)\1表达式就等于把([\d]*)匹配到的内容再次引用,比如说([\d]*)能匹配121,而([\d]*)\1能匹配121121。
(?P<name>pattern 给分组起一个别名,在引用时比number更好引用 (?P<digit>[\d]*)表达式就是匹配任意数字,并给匹配到的数字一个digit别名。
(?P=name>) 引用别名为name的分组匹配到的字符串 其实跟\<number>有点类似,不过引用从number变成name了。比如(?P<digit>[\d]*)把匹配到的数字设置一个别名,而(?P<digit>[\d]*)(?P=digit)就是根据引用别名再次匹配。
(?#commentpattern) 允许在正则表达式中写入注释,在’(?#’ ‘)’之间的内容将被忽略。 正则表达式’(?<=abc)def’会在’abcdef’中匹配’def’。
     

包含’ \ ’的特殊序列的意义如下表:

特殊表达式序列 意义
\n 匹配一个换行符,等价于\x0a和\cJ。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如,’er\b’可以匹配”never”中的’er’,但不能匹配”verb”中的’er’。
\B 匹配非单词边界,’er\B’能匹配”verb”中的’er’,但不能匹配”never”中的’er’。
\d 匹配任意十进制数,相当于[0-9]。
\D 匹配任意非数字字符,相当于[^0-9]。
\s 匹配任意空白字符,相当于[ \t\n\r\f\v]。
\S 匹配任意非空白字符,相当于[^ \t\n\r\f\v]。
\w 匹配任意数字和字母,相当于[a-zA-Z0-9_]。
\W 匹配任意非数字和字母的字符,相当于[^a-zA-Z0-9_]。
\r 匹配一个回车符,等价于\x0d和\cM。

三、Python re使用

Python的re正则表达式模块定义了一系列函数,常量以及异常;同时,正则表达式被编译成‘ RegexObject ’实例,本身可以为不同的操作提供方法。接下来简要介绍一下这些函数的功能和用法。

1. compile

re.compile(pattern[, flags])

把正则表达式的模式和标识转化成正则表达式对象,就是把规则编译好,供match()和search()这两个函数使用。

re所定义的flag包括:

re.I:忽略大小写。

re.L:表示特殊字符集\w, \W, \b, \B, \s, \S依赖于当前环境。

re.M:多行模式。

re.S:即为’ . ’并且包括换行符在内的任意字符(’ . ’不包括换行符)。

re.U:表示特殊字符集\w, \W, \b, \B, \d, \D, \s, \S依赖于Unicode字符属性数据库。

re.X:为了增加可读性,忽略空格和’ # ’后面的注释。

例:以下两种用法结果相同,只是第一种编译过,重复使用效率更好;第二种是没有编译过的。

第一种:

>>> pattern = re.compile(r'^a')
>>> pattern.match('abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>

当把正则表达式规则实例化之后,就会对应生成有很多实例属性与方法,如:match()、findall()、finditer()、split()、sub()、subn()、fullmatch()、search()、scanner()、flags、groupindex、groups、pattern等。

# 实例属性信息;
>>> pattern.flags
32
>>> pattern.pattern
'^a'

第二种

>>> re.match(r'^a', 'abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>

上面两种方式,任意一种如果匹配成功,则会返回一个对象,以及匹配范围和匹配到的值。接下来就可以把此对象实例化:

>>> pattern = re.compile(r'a')
>>> data = pattern.match('abc abc')

对应也会生成很多实例属性和方法,如:end()、expand()、group()、groupdict()、groups()、span()、start()、endpos、pos、re、regs、string、lastgroup、lastindex等。

# 显示被匹配字符串;
>>> data.string
'abc abc' # 显示匹配规则实例;
>>> data.re
re.compile('^a') # 显示匹配结果;
>>> data.group()
'a' # 匹配结果在原字符串中的索引位置;
>>> data.span()
(0, 1) # 显示从什么索引位置开始匹配;
>>> data.start()
0 # 显示匹配到什么索引位置结束;
>>> data.end()
1

对于groups()方法,是把匹配结果以组的方式返回,是一个元祖;但有个条件就是正则表达式以组的形式匹配才行,如下:

>>> pattern = re.compile(r'a(\w*) a(\w*)')
>>> data = pattern.match('abc abc')
>>> data.group()
'abc abc'
>>> data.groups()
('bc', 'bc')

对于groupdict()方法,是把匹配结果以dict方式显示;但正则匹配条件必须以组形式匹配,并且赋值一个key才行,如下:

>>> data = re.match(r'(?P<mail>[a-zA-Z0-9]{6,11}@163.com)', '12100231231@163.com')
>>> data.groupdict()
{'mail': '12100231231@163.com'}

更多属性与方法自己尝试

# 可以看到,我们在写规则时都在前面加了一个‘r’字符,是为了用来表明r”内的字符都无须转义,不然当我们包含一个’\n’时可能就会被转义为换行符,就无法做正确匹配操作了。

2. search

re.search(pattern, string[, flags])

在字符串中查找匹配正则表达式模式的位置,返回MatchObject的实例,如果没有找到匹配的位置,则返回None。

第一个参数:匹配规则

第二个参数:表示要匹配的字符串

第三个参数:标致位,用于控制正则表达式的匹配方式,比如上面介绍的大小写,多行匹配等

对于已编译的正则表达式对象来说(re.RegexObject),有方法:search (string[, pos[, endpos]])

若regex是已编译好的正则表达式对象,regex.search(string, 0, 50)等同于regex.search(string[:50], 0)。

具体示例如下:

>>> pattern = re.compile(r"a") 

# 匹配成功;
>>> pattern.search("abcde")
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>> pattern.search("bcade")
<_sre.SRE_Match object; span=(2, 3), match='a'> # 未匹配成功;
>>> pattern.search("abcde", 1) # \b界定符使用;
>>> re.search(r'de\b', "abcde")
<_sre.SRE_Match object; span=(3, 5), match='de'>
>>> re.search(r'de\b', "abcdeef")

3. match

re.match(pattern, string[, flags])

尝试从字符串的起始位置尝试匹配一个正则表达式,也等于说是从第一个字符开始匹配。

第一个参数:匹配规则

第二个参数:表示要匹配的字符串

第三个参数:标致位,用于控制正则表达式的匹配方式,比如上面介绍的大小写,多行匹配等

>>> re.match('a','ab bc cd')  #能匹配到;
>>> re.match('b','ab bc cd') #不能匹配到;

对于已编译的正则表达式对象来说(re.RegexObject),有方法:match (string[, pos[, endpos]])

match()函数只在字符串的开始位置尝试匹配正则表达式,也就是只报告从位置0开始的匹配情况,而search()函数是扫描整个字符串来查找匹配。如果想要搜索整个字符串来寻找匹配,应当用search()。

案例一:匹配特殊符号

 >>> re.match(r"\[[\w]\]", '[a]')
<_sre.SRE_Match object; span=(0, 3), match='[a]'>

案例二:匹配IP地址

>>> ip = "172.16.10.1"
>>> re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', ip)
<_sre.SRE_Match object; span=(0, 11), match='172.16.10.1'>

案例三:匹配邮箱地址

>>> re.match(r'[a-zA-Z0-9]{6,11}@[a-z0-9]*\.[a-z]{1,3}', '12100231231@163.com')
<_sre.SRE_Match object; span=(0, 19), match='12100231231@163.com'> >>> re.match(r'[a-zA-Z0-9]{6,11}@[a-z0-9]*\.com$', '12100231231@163.com')
<_sre.SRE_Match object; span=(0, 19), match='12100231231@163.com'>

案例三:分组匹配

>>> data = re.match(r'[a-zA-Z0-9]{6,11}@(163|126).com', '12100231231@163.com')
>>> data.groups()
('',) >>> data = re.match(r'[a-zA-Z0-9]{6,11}@(163|126).com', '12100231231@126.com')
>>> data.groups()
('',) >>> data = re.match(r'[a-zA-Z0-9]{11}@(.*),[\w]*@(.*)', '12100231231@163.com,abc@126.com')
>>> data.group()
'12100231231@163.com,abc@126.com'
>>> data.groups()
('163.com', '126.com')

案例四:分组匹配并起别名

>>> data = re.match(r'(?P<name>\w*) "(?P<mail>[a-zA-Z0-9]{6,11}@[a-z0-9]*\.[a-z]{1,3})" (?P<age>\d{1,3})', 'dkey "12100231231@163.com" 23')
>>> data.groupdict()
{'mail': '12100231231@163.com', 'name': 'dkey', 'age': ''}

4. split

re.split(pattern, string[, maxsplit=0, flags=0])

此功能很常用,可以将正则表达式匹配的部分进行分割,并返回一个列表。我们在python中,使用str的方法split也可以做字符串分割,但是使用正则会方便很多。

第一个参数:匹配规则

第二个参数:字符串

第三个参数:最大分割次数,默认为0,表示每个匹配项都分割

对于已编译的正则表达式对象来说(re.RegexObject),有方法:split(string[, maxsplit=0])

例如,利用上面章节中介绍的语法:

# 以:或空格分割;
>>> program = "ywnds:C C++ Java Python"
>>> re.split(r':| ', program)
['ywnds', 'C', 'C++', 'Java', 'Python'] # 以:或空格或,分割;
>>> program = "ywnds:C C++ Java Python,Go"
>>> re.split(r':| |,', program)
['ywnds', 'C', 'C++', 'Java', 'Python', 'Go']

对于一个找不到匹配的字符串而言,split不会对其作出分割,如:

>>> re.split(r'a', 'hello world')
['hello world']

5. findall

re.findall(pattern, string[, flags])

在字符串中查找到正则表达式所匹配的所有字符,并组成一个列表返回。跟search方法最大的区别就在于search只会查找到第一个匹配值后就返回,而findall是查找所有。

第一个参数:匹配规则

第二个参数:目标字符串

但三个参数:后面还可以跟一个规则选择项

对于已编译的正则表达式对象来说(re.RegexObject),有方法:findall(string[, pos[, endpos]])

示例如下:

>>> re.findall(r"\d+", 'python=90, java=80, go=70')
['', '', ''] >>> re.search(r"\d+", 'python=90, java=80, go=70')
<_sre.SRE_Match object; span=(7, 9), match=''>

6. finditer

 re.finditer(pattern, string[, flags])

和findall方法类似,在字符串中找到正则表达式所匹配的所有字符,并组成一个迭代器返回。

对于已编译的正则表达式对象来说(re.RegexObject),有方法:finditer(string[, pos[, endpos]])

7. sub

re.sub(pattern, repl, string[, count, flags])

将字符串中匹配到正则表达式的部分用另一个字符串repl进行替换。如果没有找到匹配pattern的字符,则返回未被修改的string。repl既可以是字符串也可以是一个函数。

第一个参数:匹配规则

第二个参数:替换后的字符串

第三个参数:字符串

第四个参数:替换个数,默认为0,表示每个匹配项都替换

对于已编译的正则表达式对象来说(re.RegexObject),有方法:sub(repl, string[, count=0])

此语法的示例有:

>>> p = re.compile(r'(one|two|three)')
>>> p.sub( 'num', 'one word two words three words')
'num word num words num words'

同样可以用以下方法,并指定count为1(只替换第一个):

>>> p.sub( 'num', ' one word two words three words', count=1)
' num word two words three words'

上面说了,repl也可以是一个函数,下面我们测试一下看看:

def add(v):
var = v.group()
num = int(var) + 1
return str(num)

上面定义了一个函数,默认会接收一个实例化对象,必须返回一个str对象。

>>> incr = "score = 90"
>>> incr = re.sub(r'[\d]+', add, incr)
>>> incr
'score = 91'
>>> incr = re.sub(r'[\d]+', add, incr)
>>> incr
'score = 92'

8. subn

subn(pattern, repl, string[, count, flags])

该函数的功能和sub()相同,但它还返回新的字符串以及替换的次数。

对于已编译的正则表达式对象来说(re.RegexObject),有方法:subn(repl, string[, count=0])

参考

正则表达式(常用案例)

正则表达式30分钟入门教程

Python专题教程:正则表达式re模块详解

Python面试题之Python正则表达式re模块的更多相关文章

  1. 千万不要错过这几道Python面试题,Python面试题No16

    第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...

  2. Python面试题之Python面试题汇总

    在这篇文章中: Python基础篇 1:为什么学习Python 2:通过什么途径学习Python 3:谈谈对Python和其他语言的区别 Python的优势: 4:简述解释型和编译型编程语言 5:Py ...

  3. python面试题之Python支持什么数据类型?

    所属网站分类: 面试经典 > python 作者:外星人入侵 链接:http://www.pythonheidong.com/blog/article/67/ 来源:python黑洞网,专注py ...

  4. Python面试题之Python正则表达式指南

    1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...

  5. Python面试题之Python对象反射、类反射、模块反射

    python面向对象中的反射:通过字符串的形式操作对象相关的属性.python中的一切事物都是对象(都可以使用反射) 一.getattr 对象获取 class Manager: role = &quo ...

  6. python面试题三:Python 网络编程与并发

    1 简述 OSI 七层协议. OSI七层协议模型主要是: 应用层(Application):为用户的应用程序(例如电子邮件.文件传输和终端仿真)提供网络服务. 表示层(Presentation):使用 ...

  7. Python面试题之python是一种什么语言及优缺点

    1.说说python是一种什么语言? 参考答案:python是一门动态解释性的强类型定义语言 编译型vs解释型 编译型优点:编译器一般会有预编译的过程对代码进行优化.因为编译只做一次,运行时不需要编译 ...

  8. Python面试题之Python反射机制

    0x00 前言 def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') def f4(): print('f4') a = ...

  9. Python面试题之Python面向对象编程汇总

    面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的.Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念, ...

随机推荐

  1. 利用CSS3制作淡入淡出动画效果

    CSS3新增动画属性“@-webkit-keyframes”,从字面就可以看出其含义——关键帧,这与Flash中的含义一致. 利用CSS3制作动画效果其原理与Flash一样,我们需要定义关键帧处的状态 ...

  2. java面向对象基础回顾

    (49)  (0) 面向对象 啥是面向对象 什么是多态多态的机制 接口和抽象类区别 个人理解 代码实现 面向对象 学习java大家接触到的最多的话语无非就是面向对象,可能大家没有仔细研究过这个问题,但 ...

  3. Code Force 21B Intersection

    B. Intersection time limit per test1 second memory limit per test256 megabytes inputstandard input o ...

  4. oracle导入csv文件

    oracle导入csv文件: 1.建好对应的表和字段: 2.新建test.ctl文件,用记事本编辑写入: load data infile 'e:\TB_KC_SERV.csv' --修改对应的文件路 ...

  5. C# ArcEngine 实现点击要素高亮并弹出其属性

    本文是模仿ArcMap里面的Identify(识别)功能,通过点击要素,使其高亮显示并弹出其属性表!本文只做了点击查询! 本文所用的环境为VS2010,AecEngine基于C#语言,界面是用Dev做 ...

  6. Spark源码分析 – Dependency

    Dependency 依赖, 用于表示RDD之间的因果关系, 一个dependency表示一个parent rdd, 所以在RDD中使用Seq[Dependency[_]]来表示所有的依赖关系 Dep ...

  7. 排序算法review<1>--直接插入排序

    简单插入排序的基本思想:对于原待排序记录中的第i(1<=i<=n-1)个元素Ki,保证其前面的i个元素已经是有序的,要在这前i个元素(K0--Ki-1)中找到合适的位置将第i个元素插入,具 ...

  8. ubuntu 编辑pdf

    参考:https://www.2cto.com/kf/201710/689121.html Ubuntu下几个功能比较全面的PDF编辑工具. 1.flpsed flpsed是一个WYSIWYG的“伪” ...

  9. LInux中的物理内存管理

    2017-02-23 一.伙伴系统 LInux下用伙伴系统管理物理内存页,伙伴系统得益于其良好的算法,一定程度上可以避免外部碎片为何这么说?先回顾下Linux下虚拟地址空间的分布. 在X86架构下,系 ...

  10. etcd: request cluster ID mismatch错误解决【只适用于新建etcd集群或无数据集群】

    1.报错信息 Mar 29 05:45:31 xxx etcd: request cluster ID mismatch (got 414f8613693e2e2 want cdf818194e3a8 ...