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

 更新时间:2024年12月02日 15:31:28   作者:韦易笑  
在Python中正则写复杂了很麻烦,难写难调试,其实只需要两个函数,就能用简单正则组合构建复杂正则,感兴趣的小伙伴可以跟随小编一起学习一下

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

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

# rules definition
rules = r'''
    protocol = http|https
    login_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 = 0
    size = len(pattern)
    while pos < size:
        ch = pattern[pos]
        if ch == '\\':
            output.append(pattern[pos:pos + 2])
            pos += 2
            continue
        elif ch != '{':
            output.append(ch)
            pos += 1
            continue
        p2 = pattern.find('}', pos)
        if p2 < 0:
            output.append(ch)
            pos += 1
            continue
        p3 = p2 + 1
        name = pattern[pos + 1:p2].strip('\r\n\t ')
        if name == '':
            output.append(pattern[pos:p3])
            pos = p3
            continue
        elif name[0].isdigit():
            output.append(pattern[pos:p3])
            pos = p3
            continue
        elif ('<' 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 = p3
    return ''.join(output)

# 给定规则文本,构建规则字典
def regex_build(code, macros = None, capture = True):
    defined = {}
    if macros is not None:
        for k, v in macros.items():
            defined[k] = v
    line_num = 0
    for line in code.split('\n'):
        line_num += 1
        line = line.strip('\r\n\t ')
        if (not line) or line.startswith('#'):
            continue
        pos = 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] = pattern
        else:
            defined[head] = '(?P<%s>%s)'%(head, pattern)
    return defined

# 定义一套组合规则
rules = r'''
    protocol = http|https
    login_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 行代码。

到此这篇关于Python如何使用组合方式构建复杂正则的文章就介绍到这了,更多相关Python构建复杂正则内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python+matplotlib实现计算两个信号的交叉谱密度实例

    Python+matplotlib实现计算两个信号的交叉谱密度实例

    这篇文章主要介绍了Python+matplotlib实现计算两个信号的交叉谱密度实例,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • python性能测试工具locust的使用

    python性能测试工具locust的使用

    这篇文章主要介绍了python性能测试工具locust的使用,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2020-12-12
  • python常用的正则表达式大全

    python常用的正则表达式大全

    正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配,下面这篇文章主要给大家介绍了关于python常用正则表达式的相关资料,文中通过图文以及实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • 一文带你搞懂Numpy中的深拷贝和浅拷贝

    一文带你搞懂Numpy中的深拷贝和浅拷贝

    深拷贝和浅拷贝是Python中重要的概念,本文将重点介绍在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理,快跟随小编一起来学习一下吧
    2022-04-04
  • 教你如何在Pycharm中导入requests模块

    教你如何在Pycharm中导入requests模块

    这篇文章主要介绍了教你如何在Pycharm中导入requests模块,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • 在Django的URLconf中使用命名组的方法

    在Django的URLconf中使用命名组的方法

    这篇文章主要介绍了在Django的URLconf中使用命名组的方法,Django是Pyhton各色高人气开发框架中最为著名的一个,需要的朋友可以参考下
    2015-07-07
  • Python条件语句与循环语句

    Python条件语句与循环语句

    这篇文章主要介绍了Python条件语句与循环语句,条件语句就是通过指定的表达式的运行结果来判断当前是执行还是跳过某些指定的语句块,循环语句就是对某些语句的重复执行,这个重复执行是通过指定表达式来控制的,下面来看具体内容及续航管案例吧,需要的朋友可以参考一下
    2021-11-11
  • python求列表对应元素的乘积和的实现

    python求列表对应元素的乘积和的实现

    这篇文章主要介绍了python求列表对应元素的乘积和的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • python时间整形转标准格式的示例分享

    python时间整形转标准格式的示例分享

    这篇文章主要介绍了python时间整形转标准格式的示例,需要的朋友可以参考下
    2014-02-02
  • 如何用python删除csv文件中的某几列或行

    如何用python删除csv文件中的某几列或行

    这篇文章主要给大家介绍了关于如何用python删除csv文件中的某几列或行的相关资料,在Python中我们常常需要对csv文件进行操作,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07

最新评论