首页 > 其他分享 >正则表达式(分组、零宽断言)

正则表达式(分组、零宽断言)

时间:2024-08-05 22:29:28浏览次数:13  
标签:匹配 正则表达式 res 6.3 CSDN print 分组 零宽

 

目录

  • 正则表达式分组
    • 捕获组
      • 编号捕获组 (pattern)
      • 命名捕获组 (?\<name>pattern)
    • 非捕获组 (?:pattern)
  • 零宽断言
    • 先行断言
      • 零宽正向先行断言 (?=pattern1)pettern2
      • 零宽负向先行断言 (?!pattern1)pettern2
    • 后行断言
      • 零宽正向后行断言 (?<=pattern1)pettern2
      • 零宽负向后行断言 (?<!pattern1)pettern2

 

正则表达式分组

分组格式:一对小括号()表示。

  • 一个正则表达式中有几个()就表示有几个分组。
    ():表示有1个分组,匹配空字符串。
    (\w)(\w)(\w):表示有3个分组。
  • 分组(捕获组)索引以1开始,0表示整个正则表达式组。
    1表示你定义的第一个分组,以此类推。

分组类型:捕获组(Capturing Groups)与非捕获组Non-Capturing Groups。

分组目的:缓存捕获数据、反向引用、拆分匹配数据。

捕获组

我们通常所说的分组,默认就是指捕获组,用一对小括号(pattern)表示。
捕获组类型:

  • 编号捕获组Numbered Capturing Groups
  • 命名捕获组Named Capturing Groups

是否消耗原始字符串:是。

编号捕获组 (pattern)

  • 整体匹配(非分组模式,做对比展示)

    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0"
    pattern = "\w+_\d\.\d\.\d"
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 10), match='CSDN_6.3.0'>
    print(res.groups())  # ()          分组匹配为空
    print(res.group(0))  # CSDN_6.3.0  整体匹配结果
    # print(res.group(1))  # 无分组,会报错“IndexError: no such group”
    
  • 拆分并缓存捕获数据

    拆分匹配到的数据:

    • 将整体表达式 “\w+\d.\d.\d” ,拆分为 “(\w+)(\d.\d.\d)”,包含分组一(\w+) ,分组二(\d.\d.\d)

    缓存捕获数据:

    • 将分组中的正则表达式匹配到的内容,保存到该分组里面。
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0"
    pattern = "(\w+)_(\d\.\d\.\d)"
    res = re.match(pattern, raw_string)
    print(res)  # <re.Match object; span=(0, 10), match='CSDN_6.3.0'>
    print(res.groups())  # ('CSDN', '6.3.0') 两个分组
    print(res.group(0))  # CSDN_6.3.0  整体匹配结果
    print(res.group(1))  # CSDN        分组1匹配结果
    print(res.group(2))  # 6.3.0       分组2匹配结果
    
  • 反向引用 \1

    引用格式:\number

    • number: 分组编号,从1开始,如:\1表示引用第一个分组的匹配结果

    作用:当原始字符串中出现不连续的相同子字符串时,可以通过分组引用的方式,简化匹配模式。

    特别注意:只引用匹配结果,而不是引用匹配模式。

    # 例1:CSDN_6.3.0_CSDN
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    pattern = r"(\w+)_(\d\.\d\.\d)_\1"
    res = re.match(pattern, raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())  # ('CSDN', '6.3.0') 两个分组
    print(res.group(0))  # CSDN_6.3.0_CSDN   整体匹配结果
    print(res.group(1))  # CSDN              分组1匹配结果
    print(res.group(2))  # 6.3.0             分组2匹配结果
    # print(res.group(3))                    报错,分组引用时,不会创建分组。\1引用的是匹配结果“CSDN”。
    
    # 例2:<html>text</html>
    import re
    raw_string = "<html>testtext</html>"
    pattern = r"<(\w+)>\w+</\1>"
    res = re.match(pattern, raw_string)
    print(res)  # <re.Match object; span=(0, 27), match='<html>thisistesttext</html>'>
    print(res.groups())  # ('html',)                     两个分组
    print(res.group(0))  # <html>thisistesttext</html>   整体匹配结果
    print(res.group(1))  # html                          分组1匹配结果
    # print(res.group(2))                                报错,分组引用时,不会创建分组。\1引用的是匹配结果“html”。
    

命名捕获组 (?<name>pattern)

语法:(?P<name>pattern)
优点:用命名捕获组会比较直观,可以用名称描述捕获到的内容的含义。

  • 基本使用

    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0"
    pattern = r"(?P<n1>\w+)_(?P<n2>\d\.\d\.\d)"
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 10), match='CSDN_6.3.0'>
    print(res.groups())     # ('CSDN', '6.3.0')
    print(res.group(0))     # CSDN_6.3.0
    print(res.group("n1"))  # CSDN
    print(res.group("n2"))  # 6.3.0
    
  • 反向引用

    语法:

    • python:(?P=name)
    • 其他用法:\name、\k、\k’name’
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    pattern = r"(?P<n1>\w+)_(?P<n2>\d\.\d\.\d)_(?P=n1)"
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN', '6.3.0')
    print(res.group(0))     # CSDN_6.3.0_CSDN
    print(res.group("n1"))  # CSDN
    print(res.group("n2"))  # 6.3.0
    # print(res.group(3))   # 报错,分组引用时,不会创建分组。(?P=n1)引用的是匹配结果“CSDN”。
    

非捕获组 (?:pattern)

在捕获组的基础上,在左括号的右侧加上?:,即:(?:pattern)。
不会把正则匹配到的内容保存到分组里面。提升效率。
是否消耗原始字符串:是。
是否保存匹配到的字符串到分组:否。

  • 基础用法

    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    pattern = r"(?P<n1>\w+)_(?:\d\.\d\.\d)_(?P=n1)"
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN', )  未保存第二个分组
    print(res.group(0))     # CSDN_6.3.0_CSDN
    print(res.group("n1"))  # CSDN
    # print(res.group(1))   # 报错,非捕获分组不会保存匹配的数据,且分组引用时,也不会创建分组。(?P=n1)引用的是匹配结果“CSDN”。
    
  • 使用场景说明

    • 非捕获组和零宽断言的区别:
      • 非捕获组消耗原始字符串,零宽断言不消耗原始字符串。
    • 非捕获组和零宽断言的相同点:
      • 都不会创建分组,不会保存匹配结果到分组。

零宽断言

一种特殊形式的正则表达式,匹配时不消耗原始字符,只判断pattern1是否匹配成功,如同:^ $ \b等边界匹配一样。
然后根据pattern1匹配结果进行断言,断言结果为“成功”或者“失败”,作为是否进行下一步匹配的条件

  • 若断言“成功”,继续进行正则匹配,返回匹配结果。
  • 若断言“失败”,则终止匹配行为,返回为空。

4种场景:

  • 零宽正向先行断言
  • 零宽负向先行断言
  • 零宽正向后行断言
  • 零宽负向后行断言

零宽:表示不消耗原始字符串。去除pattern1后正则表达式依旧能够匹配到目标字符串。
断言:对pattern1匹配结果进行正/负向判断。
正向:表示pattern1匹配到字符串时,断言结果为true。
负向:与正向相反,表示pattern1匹配到字符串时,断言结果为false。

先行断言

断言先于匹配位置发生。相对于pattern1表达式后面的表达式(pattern2)而言。
pattern0捕获匹配 -> pattern1先行断言 -(先于pattern2判断)->pattern2捕获匹配->返回匹配结果。
即:
step1:先根据pattern0匹配原始字符串。
step2:用pattern1匹配pattern2左侧的字符串,然后对pettern1匹配结果进行断言,若断言失败,则返回:None。反之,则继续下一步pattern2的匹配。
step3:若pattern2匹配成功,则返回整合表达式的匹配结果,反之,返回结果为:None。

零宽正向先行断言 (?=pattern1)pettern2

满足pattern1匹配,为成功。
不满足pattern1匹配,为失败。

  • 示例
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(\w+)_(?=\d\.\d\.\d)(.+)"
    # 匹配逻辑:pattern0捕获匹配 -> pattern1先行断言 -(判断是否成功)->pattern2捕获匹配
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN', '6.3.0_CSDN') 未保存第二个分组
    print(res.group(0))     # CSDN_6.3.0_CSDN
    print(res.group(1))     # CSDN
    print(res.group(2))     # 6.3.0_CSDN
    # print(res.group(3))   # 报错,零宽断言不创建分组。
    

    匹配步骤:

    • step1:先根据pattern0匹配到第1个“CSDN”
    • step2:然后判断pattern1是否匹配成功CSDN_后面的字符串(匹配到“6.3.0”,成功)
    • step3:然后进行pattern2匹配(“.+”从“CSDN_”字符串开始匹配,匹配到“6.3.0_CSDN”)。

零宽负向先行断言 (?!pattern1)pettern2

满足pattern1匹配,为失败。
不满足pattern1匹配,为成功。
解释: pattern1匹配结果,进行“非”运算,结果与正向相反。

  • 示例1:断言成功
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(\w+)_(?!AAA)(.+)"
    # 匹配逻辑:pattern0捕获匹配 -> pattern1先行断言 -(先于pattern2判断)->pattern2捕获匹配
    res = re.match(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN', '6.3.0_CSDN') 未保存第二个分组
    print(res.group(0))     # CSDN_6.3.0_CSDN
    print(res.group(1))     # CSDN
    print(res.group(2))     # 6.3.0_CSDN
    # print(res.group(3))   # 报错,零宽断言不创建分组。
    

    匹配步骤:

    • step1:先根据pattern0匹配到第1个“CSDN”
    • step2:然后判断pattern1是否匹配成功CSDN_后面的字符串(匹配不到“AAA”,成功)
    • step3:然后进行pattern2匹配(“.+”从“CSDN_”字符串开始匹配,匹配到“6.3.0_CSDN”)。
  • 示例2:断言失败
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(\w+)_(?!\d\.\d\.\d)(.+)"
    # 匹配逻辑:pattern0捕获匹配 -> pattern1先行断言 -(先于pattern2判断)-> pattern2捕获匹配
    res = re.match(pattern,raw_string)
    print(res)            # None
    print(res.groups())   # 报错,res为None。
    

    匹配步骤:

    • step1:先根据pattern0匹配到第1个“CSDN”
    • step2:然后判断pattern1是否匹配成功CSDN_后面的字符串(匹配到“6.3.0”,成功)
    • step3:不会进行pattern2匹配,返回匹配结果为:None。

后行断言

断言晚于匹配位置发生。相对于pattern1表达式后面的表达式(pattern2)而言。
即:
step1:先根据正则表达式匹配到pattern2,若pattern2匹配失败,则返回:None。反之,则进行下一步pattern1匹配断言。
step2:用pattern1匹配pattern2左侧的字符串,然后对pettern1匹配结果进行断言。
step3:若断言成功,则返回整个表达式匹配结果。反之,断言失败,返回结果为:None。

零宽正向后行断言 (?<=pattern1)pettern2

  • 示例1:断言成功
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(CSDN_\d\.\d\.\d)(?<=\d\.\d\.\d)_CSDN"
    # 匹配逻辑:pattern0捕获匹配 -> pattern2捕获匹配 -> pattern1后行断言(晚于pattern2)
    res = re.search(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN_6.3.0',) 
    print(res.group(0))     # CSDN_6.3.0_CSDN   匹配结果
    print(res.group(1))     # CSDN_6.3.0        分组值
    print(res.group(2))     # 报错,零宽断言不创建分组。
    

    匹配步骤:

    • step1:先根据"CSDN_(\d.\d.\d)_CSDN进行匹配,匹配到“CSDN_6.3.0_CSDN”。
    • step2:断言:判断pattern1是否成功匹配“_CSDN”前字符串(匹配到“6.3.0”,成功)。
    • step3:返回step1匹配的结果。

零宽负向后行断言 (?<!pattern1)pettern2

  • 示例1:断言成功
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(CSDN_\d\.\d\.\d)(?<!AAA)_CSDN"
    # 匹配逻辑:pattern0捕获匹配 -> pattern2捕获匹配 -> pattern1后行断言(晚于pattern2)
    res = re.search(pattern,raw_string)
    print(res)  # <re.Match object; span=(0, 15), match='CSDN_6.3.0_CSDN'>
    print(res.groups())     # ('CSDN_6.3.0',) 
    print(res.group(0))     # CSDN_6.3.0_CSDN   匹配结果
    print(res.group(1))     # CSDN_6.3.0        分组值
    print(res.group(2))     # 报错,零宽断言不创建分组。
    

    匹配步骤:

    • step1:先根据"CSDN_(\d.\d.\d)_CSDN进行匹配,匹配到“CSDN_6.3.0_CSDN”。
    • step2:断言:判断pattern1是否成功匹配“_CSDN”前的字符串(未匹配到“AAA”,成功)。
    • step3:则返回step1匹配的结果。
  • 示例2:断言失败
    import re
    # 原始字符串 = 产品名称_发布版本
    raw_string = "CSDN_6.3.0_CSDN"
    # pattern0(?<=pattern1)pettern2
    # 表达式技巧:去除零宽断言pattern1后,可以正常匹配字符串。
    pattern = r"(CSDN_\d\.\d\.\d)(?<!\d\.\d\.\d)_CSDN"
    res = re.search(pattern,raw_string)
    print(res)  # None
    

    匹配步骤:

    • step1:先根据"CSDN_(\d.\d.\d)_CSDN进行匹配,匹配到“CSDN_6.3.0_CSDN”。
    • step2:断言:判断pattern1是否成功匹配“_CSDN”前的字符串(匹配到“6.3.0”,失败)。
    • step3:返回结果为:None。

 

2024-08-05 22:23:14【出处】:https://blog.csdn.net/qq_40214669/article/details/136911386

=======================================================================================

标签:匹配,正则表达式,res,6.3,CSDN,print,分组,零宽
From: https://www.cnblogs.com/mq0036/p/18344169

相关文章

  • 【日常开发】一个list集合 根据a字段 b字段进行分组 并计算c字段的和 并封装这种格式:
    ......
  • 正则表达式
    正则表达式目录正则表达式字符通配符次数通配符字符类定位符分组和量词选择和逻辑运算符边界匹配符转义特殊字符预定义字符类字符通配符.:匹配任意单个字符(除了换行符)。次数通配符*:前一个字符的0次或多次。例如,a*可以匹配"cat"中的"c",也可以匹配"apple"中的"app"......
  • Apple开发_正则表达式相关
    NSString+Regex.h#import<Foundation/Foundation.h>//正则表达式相关@interfaceNSString(Regex)//邮箱验证-(BOOL)is_Email;//手机号码验证-(BOOL)is_Phone_Num;//车牌号验证-(BOOL)is_Car_No;//网址验证-(BOOL)is_Url;//邮政编码-(BOOL)is_......
  • 正则表达式
    正则表达式一.字符通配符字符通配符是一种在多种编程语言和工具中广泛使用的特殊字符或字符序列,它们用于匹配或比较字符串时表示一组字符的模式。字符通配符可以实现模糊匹配,使得字符串处理更加灵活和高效。在Java中,字符通配符的使用主要体现在以下几个方面:1.正则表达式中的通......
  • Python中使用正则表达式
    摘要:正则表达式,又称为规则表达式,它不是某种编程语言所特有的,而是计算机科学的一个概念,通常被用来检索和替换某些规则的文本。一.正则表达式的语法①行定位符行定位符就是用来描述字符串的边界。"^"表示行的开始,"$"表示行的结束。^tm  #tmequalTomorrowMoon可以......
  • 网络分组(Team)和网络绑定(bonding)的配置和区别
    一.网络分组(Team)的配置网络分组(Team)的运行模式,如下所示:运行模式描述循环(roundrobin)依次通过所有端口传输数据。活动备份(activebackup)通过一个端口传输数据,而其他端口则作为备份保留。负载均衡(loadbalance)使用主动Tx负载均衡和基于Berkeley数据包过......
  • 顺序消费rocketMQ(FIFO先进先出)和小技巧 取模运算的周期性特征来将数据分组。
    20240801一、顺序消费MQ(FIFO先进先出)介绍二、一个小技巧,对于取模运算,用来在几以前进行随机选取,取模运算的周期性特征来将数据分组,使用场景对于取模会重复问题一、顺序消费MQ(FIFO先进先出)介绍发送顺序和消费顺序保持一致默认情况消费方式是并发模式,会导致消息乱序......
  • 正则表达式:有没有一种方法可以提取单引号之间的所有子字符串,而不提取撇号之间的子字符
    基本上,我有一个包含英语语句的数据集。有些语句包含前导/尾随标点符号,所以我想清理和标准化它们。标准化的标准是删除所有前导/尾随标点符号,语句的第一个字符大写,并且语句以句点结尾。我有一个函数可以执行此操作并将其应用于数据集中的每一行。如果有帮助的话,这里是一个代码......
  • 请以零基础学Python 之 第二十讲 分组和贪婪匹配
    当我们处理字符串时,有时候需要根据特定的模式来分割或者提取信息。Python提供了强大的正则表达式库re,可以帮助我们实现这些复杂的字符串操作。本篇博客将介绍两个常用的正则表达式技巧:分组和贪婪匹配。分组(Grouping)在正则表达式中,分组是将多个模式单元组合为一个单元,以便......
  • 零基础学python 之 第十九讲 正则表达式
    当你开始学习Python编程时,正则表达式是一项非常强大的工具,用于处理文本数据中的模式匹配和搜索。本篇博客将带你从零开始学习如何在Python中使用正则表达式。1.什么是正则表达式?正则表达式(RegularExpression)是用于描述字符串模式的一种工具,可以用来匹配、查找、替换符合特......