• python — 正则表达式


    正则表达式

    正则的目的

    • 数据挖掘
      从一大堆文本中找到一小堆文本时。如,从文本是寻找email, ip,telephone等
    • 验证
      使用正则确认获得的数据是否是期望值。如,email、用户名是否合法等
    • 非必要时慎用正则,如果有更简单的方法匹配,可以不使用正则
    • 指定一个匹配规则,从而识别该规则是否在一个更大的文本字符串中。
    • 正则表达式可以识别匹配规则的文本是否存在
    • 还能将一个规则分解为一个或多个子规则,并展示每个子规则匹配的文本

    正则表达式的优缺点

    • 优点:提高工作效率、节省代码
    • 缺点:复杂,难于理解

    什么时候用正则表达式

    1. 在一大推文本字符串中找到自己想要的字符串
      1. 过滤 爬虫
    2. 验证输入是否合法

    re模块(标准库)函数

    查找一个匹配项

    1. search:查找任意位置 的匹配项
    2. match:必须从字符串开头匹配
    3. fullmatch:整个字符串与正则完全匹配

    re.search

    >>> import re
    >>> result = re.search("sanchuang","hello world,this is sanchuang")
    >>> result
    <_sre.SRE_Match object; span=(20, 29), match='sanchuang'>
    >>> result = re.search("sanchuang123","hello world,this is sanchuang")
    >>> result
    >>> print(result)
    None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    re.match

    • 从字符串头查找匹配项
    • 接受一个正则表达式和字符串,从主串第一个字符开始匹配,并返回发现的第一个匹配。
    • 如果字符串开始不符合正则表达式,则匹配失败,re.match返回None
    • r’ ‘ 中的r代表的是raw(原始字符串)
    • 原始字符串与正常字符串的区别是原始字符串不会将\字符解释成一个转义字符
    • 正则表达式使用原始字符很常见且有用
    match.group(default=0):返回匹配的字符串
    • group是由于正则表达式可以分拆为多个只调出匹配子集的子组。
    • 0是默认参数,表示匹配的整个串,n 表示第n个分
    match.groups()

    groups返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

    match.start()

    start方法提供了原始字符串中匹配开始的索引

    match.end()

    end方法提供了原始字符串中匹配开始的索引

      match只能从字符串开头查找,开始的部分没有,那就匹配不上
    >>> result = re.match("san.*$","hello world,this is sanchuang")
    >>> result
    >>> result = re.match("hello","hello world,this is sanchuang")
    >>> result
    <_sre.SRE_Match object; span=(0, 5), match='hello'>
    result.start()
    0   #开始的位置
    result.end()
    5   #结束的位置
    result.group()
    sanchuang   #匹配的结果
    如果匹配到了,就会返回match对象,没有匹配返回None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    re.fullmatch

    >>> result = re.match("hello","hello world,this is sanchuang")
    >>> result
    None
    >>> result = re.match("hello","hello")
    >>> result.group()
    hello
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查找多个匹配项

    1. fubdall:从字符串任意位置查找,返回一个列表
    2. finditer:从字符串任意位置查找,返回一个迭代器

    findall 和 finditer

    >>> msg = "i love pythonpython1 python2"
    >>> re.findall("python",msg)
    ['python', 'python', 'python']
    
    >>> re.finditer("python",msg)
    <callable_iterator object at 0x7fc3e6126470>
    >>> for i in re.finditer("python",msg):
    ...     print(i,i.group())
    ...
    <_sre.SRE_Match object; span=(7, 13), match='python'> python
    <_sre.SRE_Match object; span=(13, 19), match='python'> python
    <_sre.SRE_Match object; span=(21, 27), match='python'> python
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    替换

    re.sub

    将string中匹配的内容替换为新内容

    >>> msg = "i love python1 pythonyy python123 pythontt"
    >>> re.sub("python[0-9]","**",msg)
    'i love ** pythonyy **23 pythontt'
    >>> msg = "i love python1 pythonyy python123 pythontt"
    >>> re.sub("python\b","**",msg)
    >>> msg = "i love python1 pythonyy python123 pythontt"
    >>> re.sub("python\d","**",msg)
    'i love ** pythonyy **23 pythontt'
    正则表达式重用,先把正则表达式编译成一个对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    正则编译

    re.compile

    编译正则的特点:
    • 复杂的正则可复用。
    • 使用编译正则更方便,省略了参数。
    • re模块缓存它即席编译的正则表达式,因此在大多数情况下,使用compile并没有很大 的性能优势
    >>> reg = re.compile("python\d")
    >>> reg.findall(msg)
    ['python1', 'python1']
    
    • 1
    • 2
    • 3

    基本正则匹配

    最简单的正则表达式是那些仅包含简单的字母数字字符的表达式,复杂的正则可以实现强大的匹配

    区间:[]

    正则匹配区分大小写

    匹配a或b:a|b

    匹配cat或dog

    取反:[^abc]

    匹配a+非小写字母

    任意字符:“.”占位符

    匹配任何(除\n外)的单个字符,它仅仅只以出现在方括号字符组以外

    #区间匹配
    import re
    ret = re.findall("[Pp]y","Python3 pth pp ppython")  #[]中任选一个字符匹配
    print(ret)
    #按编码排序
    ret = re.findall("[A-z]y","python3 pth paaapaqzython")
    print('aaaa',ret)
    
    ret = re.findall("[^A-z]y","Python3 1ypth ppython")
    print(ret)
    
    #或匹配
    msg = "xlo soaodjas xsak dsa xx"
    print(re.findall("so|lo",msg))
    
    # . 占位符 表示除了换行符以外的任意一个字符
    ret = re.findall("p.thon","Python python pgthon pthon p thon p\nthon")
    print(ret)
    输出:
    ['Py', 'py']
    aaaa ['py', 'zy']
    ['1y']
    ['lo', 'so']
    ['python', 'pgthon', 'p thon']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    基本正则匹配 - 快捷方式

    • \A : 匹配字符串的开始
    • \b :词边界(123数字不算词边界)
    • \B : 非词边界
    • \w : 匹配单词字符
    • \W : 匹配非单词字符
    • \s : 匹配空白字符(包含,换行,空格)
    • \S : 匹配非空白字符
    • \d : 匹配数字
    • \D : 匹配非数字

    基本正则匹配 - 开始与结束

    正则表达式字符前面加r,让转义字符原样交给正则表达式引擎去匹配
    如果正则表达式有反斜杠,建议前面加r

    import re
    ret = re.findall(r'\bword',"啊word abcword 1word #word word123 $word")
    ret1 = re.findall(r'\wword',"啊word abcword 1word #word word123 $word")
    print(ret,ret1)
    输出:
    ['word', 'word', 'word'] ['啊word', 'cword', '1word']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    开始与结束
    • 匹配开始:^
    • 匹配结束:$
    msg = """Python
    python
    python123
    python345
    """
    ret = re.findall("^[Pp]ython",msg)
    print(ret)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    正则重复 - 通配符

    • ? 匹配前一项0-1次
    • + 匹配前一项n-1次
    • * 匹配前一项0-n次,任意次
    print("---------通配符---------")
    ret = re.findall("py?","py p python pyython")
    print(ret)
    
    ret = re.findall("py+","py p python pyython")
    print(ret)
    
    ret = re.findall("py*","py p python pyython")
    print(ret)
    
    #{n,m} 匹配前一项n-m次{n,} {,m}
    ret = re.findall("py{2,4}","pyyy py pyyyyypyypy")
    print(ret)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    正则标记

    • re.M多行匹配
    • re.I 忽略大小写
    • re.S 让.表示任意字符(包括换行符)
    ret = re.findall("Python",msg,re.M|re.I)
    print(ret)
    ret = re.findall("[Pp]ython",msg,re.M)
    print(ret)
    ret = re.findall("Python.*",msg,re.I)
    print(ret)
    ret = re.findall("Python.*",msg,re.I|re.S)
    print(ret)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    贪婪与非贪婪模式

    贪婪模式与非贪婪模式

    • 贪婪模式(.*): 匹配尽可能多的字符-默认模式
    • 非贪婪模式(.?): 正则 \d*?
    • 匹配出
      test1
      test2
    #默认贪婪模式 -- 尽可能匹配长的
    msg = "
    test
    bb
    tedst2
    "
    ret = re.findall("
    .*
    "
    ,msg) print(ret) ret = re.findall("
    .*?
    "
    ,msg) print(ret)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    正则分组

    当使用分组时,除了可以获得整个匹配,还能够获得选择每一个单独组,使用 () 进行分组

    ret = re.search(r"(\d{3})-(\d{3})-(\d{3})","abc123-456-789aaa")
    print(ret.group())
    print(ret.group(1))
    print(ret.group(2))
    print(ret.group(3))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    捕获分组和非捕获分组

    #捕获分组(正则表达式)(以上)
    #   分组匹配上之后,会把匹配上的数据放到内存中,并给定一个从1开始的索引
    #非捕获分组(?:正则表达式)
    #   只分组不捕获
    ret = re.search(r"(\d{3})-(?:\d{3})-(\d{3})","abc123-456-789aaa")
    print(ret.group())
    print(ret.group(1))
    print(ret.group(2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    引用分组

    使用()分用,用\0, \1, \2引用 (\0表示匹配的整个

    #使用findall 有捕获分组的话,只会显示捕获分组的内容
    msg = "aa bb aa bb"
    print(re.search(r"(\w+)\s(\w+)\s\1\s\2",msg))
    print(re.findall(r"(\w+)\s(\w+)\s\1\s\2",msg))
    msg = "aa bb aa cc"
    print(re.search(r"(\w+)\s(\w+)\s\1\s\2",msg))
    
    msg = "comyy@123.comcom123@qq.comaaa@126.combbb@163.comcc@abc.com"
    #把里面123 126 163 邮箱捞出来
    print(re.findall(r"(?:\.com)?(\w+@(?:123|126|163).com)",msg))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    正则断言

    正则表达式的断言分为:先行断言(lookahead)和后行断言(lookbehind)

    4种形式:

    • (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
    • (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
    • (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
    • (?

    零宽

    • ^python

    正向、负向

    • 正向:匹配(?=pattern)/(?<=pattern)
    • 负向:不匹配(?!pattern)/(?

    先行、后行

    • 先行:向后匹配(?=pattern)/(?!pattern)
    • 后行:向前匹配(?<=pattern)/(?

    零宽正向先行断言:(?=pattern)

    • 代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern。
    • 匹配字符串re,后面能接gular(但不匹配宽度
    #确定位置,不占用匹配宽度
    s = "sc1 hello sc2 hello"
    #匹配后面是 sc2的hello
    print(re.findall(r"\w+ hello(?= sc)",s))
    
    • 1
    • 2
    • 3
    • 4

    零宽负向先行断言:(?!pattern)

    • 代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。
    • 匹配字符串re,但是后面不能接gular
    #匹配后面不是 sc2的hello
    print(re.findall(r"\w+ hello(?! sc)",s))
    
    • 1
    • 2

    零宽正向后行断言:(?<=pattern)

    • 代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配pattern。
    • 匹配gular,但前面必须是re
    #匹配前面是sc2 的hello
    print(re.findall(r"(?<=sc2 )hello",s))
    
    • 1
    • 2

    零宽负向后行断言 :(?
    • 代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配pattern。
    • 匹配gular,但前面不能是re
    #匹配前面不是sc2 的hello
    print(re.findall(r"(?,s))
    
    • 1
    • 2
    msg = """
    1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: ens33:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:0c:29:9e:70:2c brd ff:ff:ff:ff:ff:ff
        inet 192.168.174.130/24 brd 192.168.174.255 scope global noprefixroute ens33
           valid_lft forever preferred_lft forever
        inet6 fe80::20c:29ff:fe9e:702c/64 scope link 
           valid_lft forever preferred_lft forever
    """
    print(re.findall(r"(?<=inet ).*(?=/24)",msg))
    输出:['192.168.174.130']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

  • 相关阅读:
    1.2 数据模型
    pyinstaller 打包pyqt6等ui文件为exe可执行程序的方法
    Guava Cache使用
    (1-线性回归问题)线性回归(Linear regression)Lasso回归和Ridge回归的区别
    2022-09-19 第五组 张明敏 学习笔记
    【Android进阶】14、顶部应用栏
    ICE常见编译和运行(异常)错误
    python+pyautogui—PC端自动化(二)键盘鼠标及对话框操作
    关于redux持久化的配置记录
    SpringBoot项目如何进行打包部署
  • 原文地址:https://blog.csdn.net/a1991376352/article/details/126292971