Python面试题之Python正则表达式re模块
一、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])
参考
Python面试题之Python正则表达式re模块的更多相关文章
- 千万不要错过这几道Python面试题,Python面试题No16
第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...
- Python面试题之Python面试题汇总
在这篇文章中: Python基础篇 1:为什么学习Python 2:通过什么途径学习Python 3:谈谈对Python和其他语言的区别 Python的优势: 4:简述解释型和编译型编程语言 5:Py ...
- python面试题之Python支持什么数据类型?
所属网站分类: 面试经典 > python 作者:外星人入侵 链接:http://www.pythonheidong.com/blog/article/67/ 来源:python黑洞网,专注py ...
- Python面试题之Python正则表达式指南
1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...
- Python面试题之Python对象反射、类反射、模块反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性.python中的一切事物都是对象(都可以使用反射) 一.getattr 对象获取 class Manager: role = &quo ...
- python面试题三:Python 网络编程与并发
1 简述 OSI 七层协议. OSI七层协议模型主要是: 应用层(Application):为用户的应用程序(例如电子邮件.文件传输和终端仿真)提供网络服务. 表示层(Presentation):使用 ...
- Python面试题之python是一种什么语言及优缺点
1.说说python是一种什么语言? 参考答案:python是一门动态解释性的强类型定义语言 编译型vs解释型 编译型优点:编译器一般会有预编译的过程对代码进行优化.因为编译只做一次,运行时不需要编译 ...
- Python面试题之Python反射机制
0x00 前言 def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') def f4(): print('f4') a = ...
- Python面试题之Python面向对象编程汇总
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的.Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念, ...
随机推荐
- Json与数组
今天趁着看源代码的同时,记录学习的小知识. 一.String.Split 方法有6个重载函数: 1) public string[] Split(params char[] separator)2) ...
- 【BZOJ4231】回忆树 离线+fail树+KMP
[BZOJ4231]回忆树 Description 回忆树是树. 具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母). 对一棵回忆树来说 ...
- iOS json解析中包含“\n”等解析出错
文题算是解决了,把特殊字符替换一下:-(NSString *)JSONString:(NSString *)aString { NSMutableString *s = [NSMutableSt ...
- 160229-01、web页面常用功能js实现
web页面常用功能js实现 1.网页未加载时弹出新窗口 <body onunload="window.open('http://www.a68.cn');">< ...
- LeetCode题目_Reverse Integer
最近在LeetCode上做题,写点东西记录一下,虽然自己做的都是些很水的题目,但是重在练手. 题号7:Reverse Integer,题目描述: Reverse digits of an intege ...
- Python面试应急5分钟!
不论你是初入江湖,还是江湖老手,只要你想给自己一个定位那就少不了面试!面试的重要性相信大家都知道把,这就是我们常说的“第一印象”,给大家说一下我的面试心得把,面试前的紧张是要的,因为这能让你充分准 ...
- nginx映射文件服务器文件夹
nginx映射文件服务器文件夹 普通用户A安装的nginx,yum源搭建文件服务器,新建普通用户B,其主目录是文件服务器需要访问的目录 普通用户A启动nginx无法访问B用户的文件服务器目录,提示40 ...
- Spring第五弹—–配置Spring管理的bean的作用域和生命周期
singleton (默认方式) 在每个Spring IoC容器中一个bean定义只有一个对象实例.默认情况下会在容器启动时初始化bean,但我们可以指定bean节点的lazy-init=“true” ...
- C++学习笔记-操作符重载
操作符重载(operator overloading)是一种形式的C++多态,C++将操作符重载扩展到用户自定义的类型,如允许使用+将两个自定义的对象相加,编译器将根据操作数的数目和类型决定使用那种加 ...
- php微信支付接口开发程序(流程已通)
php微信支付接口开发程序(流程已通) 来源:未知 时间:2014-12-11 17:11 阅读数:11843 作者:xxadmin [导读] 微信支付接口现在也慢慢的像支付宝一个可以利 ...