Python3 如何优雅地使用正则表达式(详解五)
非捕获组命名组
精心设计的正则表达式可能会划分很多组,这些组不仅可以匹配相关的子串,还能够对正则表达式本身进行分组和结构化。在复杂的正则表达式中,由于有太多的组,因此通过组的序号来跟踪和使用会变得困难。有两个新的功能可以帮你解决这个问题——非捕获组和命名组——它们都使用了一个公共的正则表达式扩展语法。我们先来看看这个表达式扩展语法是什么。
正则表达式的扩展语法
众所周知,Perl 5 为标准的正则表达式增加了许多强大的功能。Perl 的开发者们并不能选择一个新的元字符或者通过反斜杠构造一个新的特殊序列来实现扩展的功能。因为这样会和标准的正则表达式发生冲突。比如你想选择 & 作为扩展功能的元字符(在标准正则表达式中,& 没有特殊意义),但这样的话,已经按照标准语法写出来的正则表达式就不得不修改,因为它们中包含的 '&' 意愿上只是把它当做普通字符来匹配而已。
小甲鱼解释:看起来很是头疼的兼容性问题,Perl 的开发者们是如何解决的呢?请接着看......
最终,Perl 的开发者们决定使用 (?...) 作为扩展语法。问号 ? 紧跟在左小括号 ( 后边,本身是一个语法错误的写法,因为 ?前边没有东西可以重复,所以这样就解决了兼容性的问题(理由是语法正确的正则表达式肯定不会这么写嘛~)。然后,紧跟在 ? 后边的字符则表示哪些扩展语法会被使用。例如 (?=foo) 表示一种新的扩展功能(前向断言),(?:foo) 则表示另一种扩展功能(一个包含子串 foo 的非捕获组)。
Python 支持 Perl 的一些扩展语法,并且在此基础上还增加了一个扩展语法。如果紧跟在问号 ? 后边的是 P,那么可以肯定这是一个 Python 的扩展语法。
好,既然我们已经知道了如何对正则表达式的标准语法进行扩展,那我们回来看看这些扩展语法在复杂的正则表达式中是如何应用的。
非捕获组
第一个我们要讲的是非捕获组。有时候你知识需要用一个组来表示部分正则表达式,你并不需要这个组去匹配任何东西,这时你可以通过非捕获组来明确表示你的意图。非捕获组的语法是 (?:...),这个 ... 你可以替换为任何正则表达式。
- >>> m = re.match("([abc])+", "abc")
- >>> m.groups()
- ('c',)
- >>> m = re.match("(?:[abc])+", "abc")
- >>> m.groups()
- ()
复制代码
小甲鱼解释:“捕获”就是匹配的意思啦,普通的子组都是捕获组,因为它们能从字符串中匹配到数据。
除了你不能从非捕获组获得匹配的内容之外,其他的非捕获组跟普通子组没有什么区别了。你可以在里边放任何东西,使用重复功能的元字符,或者跟其他子组进行嵌套(捕获的或者非捕获的子组都可以)。
当你需要修改一个现有的模式的时候,(?:...) 是非常有用的。原始是添加一个非捕获组并不会影响到其他(捕获)组的序号。值得一提的是,在搜索的速度上,捕获组和非捕获组的速度是没有任何区别的。
命名组
我们再来看另外一个重要功能:命名组。普通子组我们使用序列来访问它们,命名组则可以使用一个有意义的名字来进行访问。
命名组的语法是 Python 特有的扩展语法:(?P<name>)。很明显,< > 里边的 name 就是命名组的名字啦。命名组除了有一个名字标识之外,跟其他捕获组是一样的。
匹配对象的所有方法不仅可以处理那些由数字引用的捕获组,还可以处理通过字符串引用的命名组。除了使用名字访问,命名组仍然可以使用数字序号进行访问:
- >>> p = re.compile(r'(?P<word>\b\w+\b)')
- >>> m = p.search( '(((( Lots of punctuation )))' )
- >>> m.group('word')
- 'Lots'
- >>> m.group(1)
- 'Lots'
复制代码
命名组非常好用,因为它让你可以使用一个好记的名字代替一些毫无意义的数字。下边是来自 imaplib 模块的例子:
- 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'"')
复制代码
很明显,使用 m.group('zonem') 访问匹配内容要比使用数字 9 更简单明了。
正则表达式中,反向引用的语法像 (...)\1 是使用序号的方式来访问子组;在命名组里,显然也是有对应的变体:使用名字来代替序号。其扩展语法是 (?P=name),含义是该 name 指向的组需要在当前位置再次引用。那么搜索两个单词的正则表达式可以写成 (\b\w+)\s+\1,也可以写成 (?P<word>\b\w+)\s+(?P=word):
- >>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
- >>> p.search('Paris in the the spring').group()
- 'the the'
复制代码
前向断言
我们要讲解的另一个零宽断言是前向断言,前向断言可以分为前向肯定断言和前向否定断言两种形式。
(?=...)
前向肯定断言。如果当前包含的正则表达式(这里以 ... 表示)在当前位置成功匹配,则代表成功,否则失败。一旦该部分正则表达式被匹配引擎尝试过,就不会继续进行匹配了;剩下的模式在此断言开始的地方继续尝试。
(?!...)
前向否定断言。这跟前向肯定断言相反(不匹配则表示成功,匹配表示失败)。
为了使大家更易懂,我们举个例子来证明这玩意是真的很有用。大家考虑一个简单的正则表达式模式,这个模式的作用是匹配一个文件名。我们都知道,文件名是用 . 将名字和扩展名分隔开的。例如在 fishc.txt 中,fishc 是文件的名字,.txt 是扩展名。
这个正则表达式其实挺简单的:
.*[.].*$
注意,这里用于分隔的 . 是一个元字符,所以我们使用 [.] 剥夺了它的特殊功能。还有 $,我们使用 $ 确保字符串剩余的部分都包含在扩展名中。所以这个正则表达式可以匹配 fishc.txt,foo.bar,autoexec.bat,sendmail.cf,printers.conf 等。
现在我们来考虑一种复杂一点的情况,如果你想匹配扩展名不是 bat 的文件,你的正则表达式应该怎么写呢?
我们先来看下你有可能写错的尝试:
.*[.][^b].*$
这里为了排除 bat,我们先尝试排除扩展名的第一个字符为非 b。但这是错误的开始,因为 foo.bar 后缀名的第一个字符也是 b。
为了弥补刚刚的错误,我们试了这一招:
.*[.]([^b]..|.[^a].|..[^t])$
我们不得不承认,这个正则表达式变得很难看......但这样第一个字符不是 b,第二个字符不是 a,第三个字符不是 t......这样正好可以接受 foo.bar,排除 autoexec.bat。但问题又来了,这样的正则表达式要求扩展名必须是三个字符,比如sendmail.cf 就会被排除掉。
好吧,我们接着修复问题:
.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$
在第三次尝试中,我们让第二个和第三个字符变成可选的。这样就可以匹配稍短的扩展名,比如 sendmail.cf。
不得不承认,我们把事情搞砸了,现在的正则表达式变得艰涩难懂外加奇丑无比!!
更惨的是如果需求改变了,例如你想同时排除 bat 和 exe 扩展名,这个正则表达式模式就变得更加复杂了......
当当当当!主角登场,其实,一个前向否定断言就可以解决你的难题:
.*[.](?!bat$).*$
我们来解释一下这个前向否定断言的含义:如果正则表达式 bat 在当前位置不匹配,尝试剩下的部分正则表达式;如果 bat匹配成功,整个正则表达式将会失败(因为是前向否定断言嘛^_^)。(?!bat$) 末尾的 $ 是为了确保可以正常匹配像sample.batch 这种以 bat 开始的扩展名。
同样,有了前向否定断言,要同时排除 bat 和 exe 扩展名,也变得相当容易:
.*[.](?!bat$|exe$).*$
Python3 如何优雅地使用正则表达式(详解五)的更多相关文章
- JavaScript正则表达式详解(一)正则表达式入门
JavaScript正则表达式是很多JavaScript开发人员比较头疼的事情,也很多人不愿意学习,只是必要的时候上网查一下就可以啦~本文中详细的把JavaScript正则表达式的用法进行了列表,希望 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- Java 正则表达式详解_正则表达式
body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...
- Django url配置 正则表达式详解 分组命名匹配 命名URL 别名 和URL反向解析 命名空间模式
Django基础二之URL路由系统 本节目录 一 URL配置 二 正则表达式详解 三 分组命名匹配 四 命名URL(别名)和URL反向解析 五 命名空间模式 一 URL配置 Django 1.11版本 ...
- 【python3+request】python3+requests接口自动化测试框架实例详解教程
转自:https://my.oschina.net/u/3041656/blog/820023 [python3+request]python3+requests接口自动化测试框架实例详解教程 前段时 ...
- (转)Python3.5——装饰器及应用详解
原文:https://blog.csdn.net/loveliuzz/article/details/77853346 Python3.5——装饰器及应用详解(下)----https://blog.c ...
- (转)linux正则表达式详解
linux正则表达式详解 http://blog.csdn.net/wuliowen/article/details/64131815 1:什么是正则表达式: 简单的说,正则表达式就是处理字符串的方法 ...
- Python3、setuptools、Pip3安装详解
Python3.setuptools.Pip3安装详解 2017年08月19日 18:58:47 安静的技术控 阅读数:26002 版权声明:本文为博主原创文章,未经博主允许不得转载. http ...
- Linux文本处理三剑客之grep及正则表达式详解
Linux文本处理三剑客之grep及正则表达式详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Linux文本处理三剑客概述 grep: 全称:"Global se ...
- Django路由配置之正则表达式详解
正则表达式详解 urls.py from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles ...
随机推荐
- List<T> 和DataTable的相互转换
我用的将集合类转换为DataTable 的方法 /// <summary> /// 将集合类转换成DataTable /// </summary> /// <param ...
- link@import
1.两者区别 1)link属于XHTML标签,而@import是CSS提供的;2)页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载; 3)import只在IE ...
- HDOJ(HDU) 1785 You Are All Excellent(角度运算)
Problem Description 本次集训队共有30多人参加,毫无疑问,你们都是很优秀的,但是由于参赛名额有限,只能选拔部分队员参加省赛.从学校的角度,总是希望选拔出最优秀的18人组成6支队伍来 ...
- openStack 对象存储object storage swift
- Java 8 中新的 Date 和 Time 类入门详解
这篇文章主要是java8中新的Date和Time API的实战.新的Date和Time类是java开发者社区千呼万唤始出来的.Java8 之前存在的Date类一直都受人诟病,很多人都会选择使用第三方的 ...
- java中的Package语句和import语句
在实际项目中会有成百上千个类,我们把近似的类放在同一个包里面,比如把实体类放在实体类包里面 package 为解决类的 命名冲突问题而引入的机制. package语句作为Java源文件的第一条语句 ...
- Robotium--takeScreenshot(截图)
在Robotium中,截图的方法时调用takeScreenshot(). 但有使用你会发现明明代码里调用了solo.takeScreenshot(),但却没有截图成功,那是因为被测试的应用没有SD卡的 ...
- Facebook的手游出海之道
对于不同的游戏公司,面临的同一个问题就是怎样让海外玩家能够一眼在App中发现你,成为你的新用户:不仅如此,怎样留住这些用户,让他们成为你游戏的忠实玩家也是让全部游戏开发商困扰的一个问题. w=580& ...
- [Android]android.graphics.Camera实现图像的旋转、缩放,配合Matrix实现图像的倾斜
android.graphics.Camera可以对图像执行一些比较复杂的操作,诸如旋转与绽放,与Matrix可实现图像的倾斜. 个人总结Camera与Matrix的一些区别如下: Camera的ro ...
- wpf的学习日志(一)
今天开始学习wpf,从xaml的布局开始 stackpanel布局:Orientation决定布局的横向还是纵向,HorizontalAlignment决定布局的对齐 <StackPanel O ...