查看: 274|回复: 0

Python正则表达式|逻辑或作用域与分组量词实践

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在Python正则表达式中,逻辑“或”用竖线 | 表示,用来匹配多个备选模式中的任意一个。正则表达式没有专门的逻辑与、非符号:顺序书写的字符本身构成“与”,负向先行断言(?!)、字符类中的^等可表达“非”,而|负责“或”。下面基于Python re模块的findall示例,说明|在整体分支、分组、量词组合和字符类中的不同行为。

一、用|分割整个正则的备选分支
  1. import re
  2. import time
  3. a = "abc efg hij"
  4. b = re.findall(r"abc|efg", a)
  5. c = re.findall(r'abc|efg|hij', a)
  6. print("b = {}, c = {}".format(b, c))
  7. # 输出结果:b = ['abc', 'efg'], c = ['abc', 'efg', 'hij']
复制代码
b中|连接abc和efg,只要匹配abc或efg中任意一个子串即可;c增加hij分支后,能匹配abc、efg、hij中的任意一个。这里re.findall(pattern, string)返回所有满足分支条件的子串列表。

二、用分组限制|的作用域
  1. import re
  2. a = "abc21efg123hij111"
  3. b = re.findall(r"(?:abc|efg)\d+", a)
  4. c = re.findall(r'(?:abc|efg|hij)\d+', a)
  5. print("b = {}, c = {}".format(b, c))
  6. # 输出结果:b = ['abc21', 'efg123'],
  7. # c = ['abc21', 'efg123', 'hij111']
复制代码
(?:...)是不捕获分组,findall会返回完整匹配内容。此时|只在分组内生效:b匹配“abc后跟数字”或“efg后跟数字”,c再多一个“hij后跟数字”分支。

如果去掉括号,|的作用域会扩展到整个正则表达式:
  1. import re
  2. a = "abc21efg123hij111"
  3. b = re.findall(r"abc|efg\d+", a)
  4. c = re.findall(r'abc|efg|hij\d+', a)
  5. print("b = {}, c = {}".format(b, c))
  6. # 输出结果:b = ['abc', 'efg123'],
  7. # c = ['abc', 'efg', 'hij111']
复制代码
b匹配的是abc分支,或efg\d+分支,而不是(abc|efg)\d+。这说明没有分组时,|不再只约束前面的abc或efg,而是把整个左右两侧拆成备选分支。结果中只有最后一个分支带了数字,正是作用域变化造成的。

三、|与量词组合时的贪婪和非贪婪
  1. import re
  2. a = "abcefg123efgabchij123efghij111"
  3. b = re.findall(r"(?:abc|efg)+", a)
  4. c = re.findall(r'(?:abc|efg|hij)+', a)
  5. d = re.findall(r"(?:abc|efg)+?", a)
  6. e = re.findall(r'(?:abc|efg|hij)+?', a)
  7. print("b = {}, c = {}, d = {}, e = {}".
  8. format(b, c, d ,e))
  9. # 输出结果:b = ['abcefg', 'efgabc', 'efg'],
  10. # c = ['abcefg', 'efgabchij', 'efghij'],
  11. # d = ['abc', 'efg', 'efg', 'abc', 'efg'], e
  12. # = ['abc', 'efg', 'efg', 'abc', 'hij', 'efg', 'hij']
复制代码
在分组外添加+后,量词会作用于整个分组,形成排列组合式的连续匹配。b中的+是贪婪匹配,匹配到abc后还会继续尝试efg,因此能得到abcefg、efgabc等结果;c允许abc、efg、hij三个备选连续出现。d和e在+后加?,变成非贪婪匹配,只匹配分组中的单个备选项即可,所以结果不再合并成连续组合。

如果要匹配固定次数组合,可以用{n}代替+。但固定次数仍可能匹配到abcabc、efgefg这类重复组合。此时可组合负向先行断言过滤重复:
  1. import re
  2. a = "abcefg|abchij|abcabc|hijhij|efghij|efgefg"
  3. b = re.findall(r'''(?x)(?:abc(?!abc)|
  4. efg(?!efg)|hij(?!hij)){2}''', a)
  5. print("b = {}".
  6. format(b))
  7. # 输出结果:b = ['abcefg', 'abchij', 'efghij']
复制代码
这里的字符串a中,|只是普通字符分隔符。正则开头的(?x)是详细模式,会忽略正则中的换行、回车、tab和空格,因此模式可以分行书写。abc(?!abc)表示abc后面不能紧跟abc,efg(?!efg)和hij(?!hij)同理,用来过滤abcabc、efgefg、hijhij这类两个相同组合重复出现的情况。最终只保留abcefg、abchij、efghij。

四、字符类中的|是普通字符
  1. import re
  2. a = "a1|2c2|3\\3"
  3. b = re.findall(r'[abc|]\d', a)
  4. c = re.findall(r'[abc\|]\d', a)
  5. print("b = {}, c = {}".format(b, c))
  6. # 输出结果:b = ['a1', '|2', 'c2', '|3'],
  7. # c = ['a1', '|2', 'c2', '|3']
复制代码
字符类中的每个字符本来就是“或”的关系,因此|在[abc|]中不再表示逻辑或,而是作为普通字符参与匹配。b和c的结果一致,说明在字符类中给|加反斜杠转义与不加转义效果相同,|都只代表它自身。

五、实践中的检查顺序
当re.findall结果与预期不一致时,可以先检查|是否被括号限制在局部,再检查量词作用于哪一个紧邻前项,以及是否使用了贪婪或非贪婪写法;如果|出现在字符类中,则要意识到它已失去逻辑或含义。这个排查顺序能覆盖本文示例中的主要差异:分支作用域、分组边界、量词组合和字符类语义。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-17 12:45 , Processed in 0.027834 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部