当前位置: 首页 > news >正文

Python 中使用组合方式构建复杂正则

正则写复杂了很麻烦,难写难调试,只需要两个函数,就能用简单正则组合构建复杂正则:

比如输入一个字符串规则,可以使用 {name} 引用前面定义的规则:

# rules definition
rules = r'''protocol = http|httpslogin_name = [^:@\r\n\t ]+login_pass = [^@\r\n\t ]+login = {login_name}(:{login_pass})?host = [^:/@\r\n\t ]+port = \d+optional_port = (?:[:]{port})?path = /[^\r\n\t ]*url = {protocol}://({login}[@])?{host}{optional_port}{path}?
'''

然后调用 regex_build 函数,将上面的规则转换成一个字典并输出:

# expand patterns in a dictionary
m = regex_build(rules, capture = True)# list generated patterns
for k, v in m.items(): print(k, '=', v)

结果:

protocol = (?P<protocol>http|https)
login_name = (?P<login_name>[^:@\r\n\t ]+)
login_pass = (?P<login_pass>[^@\r\n\t ]+)
login = (?P<login>(?P<login_name>[^:@\r\n\t ]+)(:(?P<login_pass>[^@\r\n\t ]+))?)
host = (?P<host>[^:/@\r\n\t ]+)
port = (?P<port>\d+)
optional_port = (?P<optional_port>(?:[:](?P<port>\d+))?)
path = (?P<path>/[^\r\n\t ]*)
url = (?P<url>(?P<protocol>http|https)://((?P<login>(?P<login_name>[^:@\r\n\t ]+)(:(?P<login_pass>[^@\r\n\t ]+))?)[@])?(?P<host>[^:/@\r\n\t ]+)(?P<optional_port>(?:[:](?P<port>\d+))?)(?P<path>/[^\r\n\t ]*)?)

用手写直接写是很难写出这么复杂的正则的,写出来也很难调试,而组合方式构建正则的话,可以将小的简单正则提前测试好,要用的时候再组装起来,就不容易出错,上面就是组装替换后的结果。

下面用里面的 url 这个规则来匹配一下:

# 使用规则 "url" 进行匹配
pattern = m['url']
s = re.match(pattern, 'https://name:pass@www.baidu.com:8080/haha')# 打印完整匹配结果
print('matched: "%s"'%s.group(0))
print()# 打印分组匹配结果
for name in ('url', 'login_name', 'login_pass', 'host', 'port', 'path'):print('subgroup:', name, '=', s.group(name))

输出:

match text with pattern "url"
matched: "https://name:pass@www.baidu.com:8080/haha"subgroup: url = https://name:pass@www.baidu.com:8080/haha
subgroup: login_name = name
subgroup: login_pass = pass
subgroup: host = www.baidu.com
subgroup: port = 8080
subgroup: path = /haha

可以取完整结果,也可以按照规则名字,取得里面具体某个部件得匹配结果。

这下可以方便的写复杂正则表达式了。

再 Python 的正则表达式里 {xxx} 是用来表示长度的,里面都是数字,如果里面是变量名的话不会和原有规则冲突,因此这个写法是安全的。

实现代码:

import re# 将 pattern 里形如 {name} 的文本,用 macros 里的预定义规则替换
def regex_expand(macros, pattern, guarded = True):output = []pos = 0size = len(pattern)while pos < size:ch = pattern[pos]if ch == '\\':output.append(pattern[pos:pos + 2])pos += 2continueelif ch != '{':output.append(ch)pos += 1continuep2 = pattern.find('}', pos)if p2 < 0:output.append(ch)pos += 1continuep3 = p2 + 1name = pattern[pos + 1:p2].strip('\r\n\t ')if name == '':output.append(pattern[pos:p3])pos = p3continueelif name[0].isdigit():output.append(pattern[pos:p3])pos = p3continueelif ('<' in name) or ('>' in name):raise ValueError('invalid pattern name "%s"'%name)if name not in macros:raise ValueError('{%s} is undefined'%name)if guarded:output.append('(?:' + macros[name] + ')')else:output.append(macros[name])pos = p3return ''.join(output)# 给定规则文本,构建规则字典
def regex_build(code, macros = None, capture = True):defined = {}if macros is not None:for k, v in macros.items():defined[k] = vline_num = 0for line in code.split('\n'):line_num += 1line = line.strip('\r\n\t ')if (not line) or line.startswith('#'):continuepos = line.find('=')if pos < 0:raise ValueError('%d: not a valid rule'%line_num)head = line[:pos].strip('\r\n\t ')body = line[pos + 1:].strip('\r\n\t ')if (not head):raise ValueError('%d: empty rule name'%line_num)elif head[0].isdigit():raise ValueError('%d: invalid rule name "%s"'%(line_num, head))elif ('<' in head) or ('>' in head):raise ValueError('%d: invalid rule name "%s"'%(line_num, head))try:pattern = regex_expand(defined, body, guarded = not capture)except ValueError as e:raise ValueError('%d: %s'%(line_num, str(e)))try:re.compile(pattern)except re.error:raise ValueError('%d: invalid pattern "%s"'%(line_num, pattern))if not capture:defined[head] = patternelse:defined[head] = '(?P<%s>%s)'%(head, pattern)return defined# 定义一套组合规则
rules = r'''protocol = http|httpslogin_name = [^:@\r\n\t ]+login_pass = [^@\r\n\t ]+login = {login_name}(:{login_pass})?host = [^:/@\r\n\t ]+port = \d+optional_port = (?:[:]{port})?path = /[^\r\n\t ]*url = {protocol}://({login}[@])?{host}{optional_port}{path}?
'''# 将上面的规则展开成字典
m = regex_build(rules, capture = True)# 输出字典内容
for k, v in m.items(): print(k, '=', v)print()# 用最终规则 "url" 匹配文本
pattern = m['url']
s = re.match(pattern, 'https://name:pass@www.baidu.com:8080/haha')# 打印完整匹配
print('matched: "%s"'%s.group(0))
print()# 按名字打印分组匹配
for name in ('url', 'login_name', 'login_pass', 'host', 'port', 'path'):print('subgroup:', name, '=', s.group(name))

完事,主要逻辑 84 行代码。


http://www.mrgr.cn/news/68839.html

相关文章:

  • 【HGT】文献精讲:Heterogeneous Graph Transformer
  • m6ATM
  • 一、初识C语言(1)
  • Spark中的宽窄依赖-宽窄巷子
  • Bash Shell - 获取日期、时间
  • go语言中package详解
  • Java 中如何巧妙应用 Function 让方法复用性更强
  • 三十五、Python基础语法(文件操作-下)
  • MIT 6.S081 Lab1: Xv6 and Unix utilities翻译
  • 关于向前欧拉法的一些总结
  • ARXML汽车可扩展标记性语言规范讲解
  • 单细胞 RNA 测序分析的当前最佳实践:教程-文献精读80
  • 【AI日记】24.11.08 Knowledge Graphs for RAG (知识图谱,Neo4j,Cypher)
  • C++builder中的人工智能(12):了解ELU(Exponential Linear Unit)——人工神经网络中的激活函数
  • 【Android】名不符实的Window类
  • AutoSar AP CM中的序列化总结
  • C++builder中的人工智能(10)神经网络中的Sigmoid函数
  • 苍穹外卖day09超出配送范围前端不提示问题
  • 367.有效地完全平方数
  • HCIP MPLS基础
  • 【JavaScript】网络请求之Promise fetch Axios及异步处理
  • YOLO11 旋转目标检测 | 数据标注 | 自定义数据集 | 模型训练 | 模型推理
  • 新一代AI换脸更自然,DeepLiveCam下载介绍(可直播)
  • C++代码优化(二): 区分接口继承和实现继承
  • 小白docker入门简介
  • 【计网不挂科】计算机网络期末考试(综合)——【选择题&填空题&判断题&简述题】完整试卷