Python正则表达式之扩展语法(5)

本文详细介绍了非捕获组和命名组在正则表达式中的应用,如何通过这些高级特性简化模式编写,提高可读性和维护性。非捕获组用于标记不需要匹配内容的子组,而命名组则提供了更具意义的引用方式,便于后期操作。
部署运行你感兴趣的模型镜像

非捕获组和命名组

精心设计的正则表达式可能会划分很多组,这些组不仅可以匹配相关的子串,还能够对正则表达式本身进行分组和结构化。在复杂的正则表达式中,由于有太多的组,因此通过组的序号来跟踪和使用会变得困难。有两个新的功能可以帮你解决这个问题——非捕获组和命名组——它们都使用了一个公共的正则表达式扩展语法。我们先来看看这个表达式扩展语法是什么。

正则表达式的扩展语法:

众所周知,Perl 5 为标准的正则表达式增加了许多强大的功能。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 中,fish 是文件的名字,.txt 是扩展名。

这个正则表达式其实挺简单的:.*[.].*$

注意,这里用于分隔的 . 是一个元字符,所以我们使用 [.] 剥夺了它的特殊功能。还有 $,我们使用 $ 确保字符串剩余的部分都包含在扩展名中。所以这个正则表达式可以匹配 fishc.txtfoo.barautoexec.batsendmail.cfprinters.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

不得不承认,我们把事情搞砸了,现在的正则表达式变得艰涩难懂外加奇丑无比!!

更惨的是如果需求改变了,例如你想同时排除 batexe 扩展名,这个正则表达式模式就变得更加复杂了…

当当当当!主角登场,其实,一个前向否定断言就可以解决你的难题:

.*[.](?!bat$).*$

我们来解释一下这个前向否定断言的含义:如果正则表达式 bat 在当前位置不匹配,尝试剩下的部分正则表达式;如果 bat 匹配成功,整个正则表达式将会失败(因为是前向否定断言嘛_)。(?!bat$) 末尾的 $ 是为了确保可以正常匹配像 sample.batch 这种以 bat 开始的扩展名

同样,有了前向否定断言,要同时排除 bat 和 exe 扩展名,也变得相当容易:

.*[.](?!bat$|exe$).*$

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think> 首先,用户要求我回答关于Python正则表达式的相关信息,包括使用、教程及相关知识。我必须基于提供的引用内容来构建回答,因为系统指令说“参考站内引用”,并给出了四个引用。 回顾引用内容: - 引用[1]:介绍正则表达式的基本概念,不是Python特有,语法通用,效率可能不如字符串方法但功能强大。 - 引用[2]:讨论Python正则表达式的反斜杠问题,需要用"\\\\"表示"\",因为转义冲突;建议使用原生字符串r"..."来简化。 - 引用[3]:进一步解释原生字符串在Python中的作用,避免转义问题。 - 引用[4]:说明使用re.compile编译正则表达式对象,并与findall()等方法结合使用。 用户的问题是:“我想获取关于Python正则表达式的相关信息 请问Python正则表达式的使用、教程及相关知识”。所以,我需要提供一个结构清晰的回答,覆盖这些方面。 系统指令要求: - 所有行内数学表达式用$...$格式(但这里可能不涉及数学,所以可能不需要)。 - 独立公式用$$...$$格式(同上,可能不适用)。 - LaTeX语法正确(注意转义)。 - 使用中文回答。 - 生成相关问题(在回答末尾添加“相关问题”部分)。 - 引用的段落末尾自然地添加引用标识,如[^1]。 在回答中,我需要自然地融入引用。引用是用户提供的,所以我应该在相关内容结束时添加标识。 构建回答结构: 1. **引言**:简要介绍Python正则表达式是什么,参考引用[1]。 2. **基本使用**:解释如何导入re模块,基本语法。 3. **转义问题**:详细说明反斜杠转义和使用原生字符串,参考引用[2]和[3]。 4. **编译正则表达式**:讨论re.compile的使用,参考引用[4]。 5. **常用方法**:简要介绍search(), match(), findall()等常用函数。 6. **示例**:提供代码示例来演示用法。 7. **相关问题**:在末尾生成相关问题。 确保回答真实可靠,基于引用内容。引用提供了具体信息,我可以扩展一下。 关键点: - Python正则表达式通过re模块实现。 - 主要功能:匹配、搜索、替换字符串。 - 语法:模式字符串,特殊字符如. * ?等。 - 重要概念:转义字符、原生字符串。 - 实践:使用re.compile优化重复使用。 生成相关问题:基于主题,如高级用法、常见陷阱、性能优化等。 最终回答格式: - 使用中文。 - 结构清晰:分段落。 - 添加引用标识。 - 末尾有“相关问题”部分。 </think> ### Python正则表达式教程:基本概念、使用及相关知识 Python正则表达式(Regular Expression,简称Regex)是一种强大的字符串处理工具,它基于通用语法规则实现模式匹配,适用于文本搜索、提取、替换等任务。Python通过内置的`re`模块提供正则表达式支持,其语法与其他语言类似,但需注意Python特有的转义机制。得益于其灵活性,正则表达式在数据处理、日志分析、网页爬虫等场景中广泛使用[^1]。下面我将逐步介绍Python正则表达式的基本使用、核心知识和实用教程。 #### 1. **基本概念和导入模块** Python正则表达式不是语言内置部分,而是通过`re`模块实现。正则表达式语法独立且通用,例如,`.`匹配任意字符,`*`表示零或多个重复。尽管Python的字符串方法(如`str.find()`)在简单场景下更高效,但正则表达式在复杂模式匹配(如提取特定格式数据)中功能更强大[^1]。使用前需导入模块: ```python import re ``` #### 2. **正则表达式语法和转义问题** - **语法基础**:正则表达式模式由普通字符(如字母、数字)和特殊字符(如`.`、`*`)组成。例如,`a.b`可以匹配"axb"或"ayb",但不能匹配"ab"。 - **转义冲突**:Python字符串使用反斜杠`\`作为转义字符,而正则表达式中`\`也表示特殊含义(如`\d`匹配数字)。这导致双重转义问题:正则表达式中的单个`\`在Python字符串中需写成`\\\\`。例如,匹配一个反斜杠字符时,写为`\\\\`在正则表达式中对应`\`[^2]。 - **原生字符串(Raw Strings)**:为避免转义混乱,Python推荐使用原始字符串前缀`r`。例如,`r"\\"`直接表示正则表达式中的`\`,简化了代码: ```python import re # 不使用原生字符串:匹配 \ pattern1 = "\\\\" # 正则表达式模式为 "\\" # 使用原生字符串:更简洁 pattern2 = r"\\" # 正则表达式模式同样为 "\\" ``` 原生字符串让模式更易读,尤其当模式包含大量反斜杠时[^3]。 #### 3. **编译和使用正则表达式** 为提高效率,Python允许使用`re.compile()`将正则表达式字符串编译为`Pattern`对象。编译后,对象可重复用于多次匹配,避免重新解析模式的开销[^4]。编译后需搭配常用方法: - **`findall()`**:查找所有匹配项,返回列表。 - **`search()`**:扫描整个字符串,返回第一个匹配对象。 - **`match()`**:仅在字符串起始位置匹配。 - **其他方法**:`sub()`用于替换,`split()`用于分割。 示例:提取文本中的括号内容 ```python import re text = """ 【代号】华法琳 【性别】女 【战斗经验】十年 【出身地】卡兹戴尔 """ # 编译正则表达式:匹配【】内的任意字符(非贪婪模式 ?) p = re.compile(r'【.*?】') # 使用原生字符串避免转义 result = p.findall(text) # 查找所有匹配 print(result) # 输出: ['【代号】', '【性别】', '【战斗经验】', '【出身地】'] ``` 此例中,`r'【.*?】'`使用非贪婪模式`.*?`匹配最短内容,避免错误捕获长字符串[^4]。 #### 4. **常用正则表达式方法和教程要点** - **基础匹配函数**: - `re.search(pattern, string)`:搜索整个字符串,适合不确定位置时。 - `re.match(pattern, string)`:仅检查字符串开头,匹配失败返回`None`。 - `re.findall(pattern, string)`:返回所有非重叠匹配项。 - **特殊字符和组**: - 字符类:`[a-z]`匹配小写字母,`\d`匹配数字。 - 分组:用`()`捕获子模式,如`(ab)+`匹配"ab"、"abab"等。 - 量词:`*`(零或多个)、`+`(一或多个)、`?`(零或一)。 - **替换和分割**:`re.sub(pattern, repl, string)`替换匹配项,`re.split(pattern, string)`按模式分割字符串。 - **性能提示**:对于多次使用的模式,优先编译`Pattern`对象;简单任务考虑Python字符串方法以提升效率[^1]。 #### 5. **实际应用场景** - **数据清洗**:从日志文件中提取IP地址(模式如`r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'`)。 - **表单验证**:检查邮箱格式(如`r'^[\w\.-]+@[\w\.-]+\.\w+$'`)。 - **网页解析**:爬虫中抽取HTML标签内容。 注意:正则表达式复杂时可能影响性能,测试时使用小样本[^1]。 正则表达式Python中处理文本的利器,但需练习掌握。建议从简单模式开始,逐步学习高级特性如lookahead等。更多教程可参考Python官方文档或在线资源[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻梦&之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值