正则表达式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式相关的知识,希望对你有一定的参考价值。

参考技术A 根据下面两个链接自己试验一遍的笔记。
https://foofish.net/re-tutorial.html
https://foofish.net/crawler-re-second

.:匹配除换行符以外的任意一个字符,例如:"a.c" 可以完全匹配 "abc",也可以匹配 "abcef" 中的 "abc" : 转义字符,使特殊字符具有本来的意义,例如: 1.2 可以匹配 1.2
[...]:匹配方括号中的任意一个字符,例如:a[bcd]e 可以匹配 abe、ace、ade,它还支持范围操作,比如:a到z可表示为 "a-z",0到9可表示为 "0-9",注意,在 "[]" 中的特殊字符不再有特殊意义,就是它字面的意义,例如:[.*]就是匹配 . 或者 *
[ ...],字符集取反,表示只要不是括号中出现的字符都可以匹配,例如:a[ bcd]e 可匹配 aee、afe等

匹配任意一个单词字符,包括数字和下划线,它等价于 [A-Za-z0-9_],例如 a可以匹配 abc、acc 匹配任意一个非单词字符,与 操作相反,它等价于 [^A-Za-z0-9_],例如: a可匹配 a!c 匹配任意一个空白字符,空格、回车等都是空白字符,例如:a可以配 a,这里的 匹配任意一个非空白字符 匹配任意一个数字,它等价于[0-9],例如:a可匹配 a1c、a2c ... 匹配任意一个非数字

^ 匹配字符的开头,在字符串的前面,例如:^abc 表示匹配 a开头,后面紧随bc的字符串,它可以匹配 abc
$匹配字符的结尾,在字符串的末尾位置

re.match(r"^abc","abc").group()

'abc'

re.match(r"^abc$","abc").group()

'abc'

*重复匹配零次或者更多次 ? 重复匹配零次或者一次 +重复匹配1次或者多次 n 重复匹配n次 n, 重复匹配至少n次 n, m 重复匹配n到m次

匹配一个固定电话号码,不同地区规则不一样,有的地方区号是3位,电话是8位,有的地方区号是4位,电话为7位,区号与号码之间用 - 隔开,如果应对这样的需求呢?这时你需要用到逻辑分支条件字符 |,它把表达式分为左右两部分,先尝试匹配左边部分,如果匹配成功就不再匹配后面部分了,这是逻辑 "或" 的关系

前面介绍的匹配规则都是针对单个字符而言的,如果想要重复匹配多个字符怎么办,答案是,用子表达式(也叫分组)来表示,分组用小括号"()"表示,例如 (abc)2 表示匹配abc两次, 匹配一个IP地址时,可以使用 (.)3,因为IP是由4组数组3个点组成的,所有,前面3组数字和3个点可以作为一个分组重复3次,最后一部分是一个1到3个数字组成的字符串。如:192.168.0.1。

关于分组,group 方法可用于提取匹配的字符串分组,默认它会把整个表达式的匹配结果当做第0个分组,就是不带参数的 group() 或者是 group(0),第一组括号中的分组用group(1)获取,以此类推

默认情况下,正则表达式重复匹配时,在使整个表达式能得到匹配的前提下尽可能匹配多的字符,我们称之为贪婪模式,是一种贪得无厌的模式。例如: r"a.*b" 表示匹配 a 开头 b 结尾,中间可以是任意多个字符的字符串,如果用它来匹配 aaabcb,那么它会匹配整个字符串。

有时,我们希望尽可能少的匹配,怎么办?只需要在量词后面加一个问号" ?",在保证匹配的情况下尽可能少的匹配,比如刚才的例子,我们只希望匹配 aaab,那么只需要修改正则表达式为 r"a.*?b"

非贪婪模式在爬虫应用中使用非常频繁。比如之前在公众号「Python之禅」曾写过一篇爬取网站并将其转换为PDF文件的场景,在网页上涉及img标签元素是相对路径的情况,我们需要把它替换成绝对路径

sub 函数可以接受一个函数作为替换目标对象,函数返回值用来替换正则表达式匹配的部分,在这里,我把整个img标签定义为一个正则表达式 r'',group() 返回的值是 img src="/images/category.png",而 group(1) 的返回值是 /images/category.png,最后,我用 replace 方法把相对路径替换成绝对路径。

match 方法从字符串的起始位置开始检查,如果刚好有一个子字符串与正则表达式相匹配,则返回一个Match对象,只要起始位置不匹配则退出,不再往后检查了,返回 None

search 方法虽然也是从起始位置开始检查,但是它在起始位置不匹配的时候会一直尝试往后检查,直到匹配为止,如果到字符串的末尾还没有匹配,则返回 None

两者接收参数都是一样的,第一个参数是正则表达式,第二个是预匹配的字符串。另外,不管是 search 还是 match,一旦找到了匹配的子字符串,就立刻停止往后找,哪怕字符串中有多个可匹配的子字符串,例如

两者的差异使得他们在应用场景上也不一样,如果是检查文本是否匹配某种模式,比如,检查字符串是不是有效的邮箱地址,则可以使用 match 来判断:

尽管第二个字符串中包含有邮件地址,但字符串整体不能当作一个邮件地址来使用,在网页上填邮件地址时,显然第二种写法是无效的。

通常,search 方法可用于判断字符串中是否包含有与正则表达式相匹配的子字符串,还可以从中提出匹配的子字符串,例如:

findall 返回的对象是由匹配的子字符串组成的列表,它返回了所有匹配的邮件地址。

re.split是一种更为高级的字符串分隔操作的方法。在这里,split根据非字母正则来分隔字符串,但凡是 string.split 没法处理的问题,可以考虑使用re模块下的split方法来处理。此外,正则表达式中如果有分组括号,那么返回结果又不一致,这个可以留给大家查阅文档,某些场景用得着。

把所有邮箱地址替换成 admin@qq.com

正则表达式基础用法整理

正则表达式

1. 正则表达式

1.1 正则表达式介绍

1.1.1 什么是正则表达式?

  • 正则表达式就是一种字符串,该字符串通过包含特殊的符号,来描述一类字符串的公共特点,进而可以使用该模式串来匹配并获取文本内容中的所有符合该特点的字符串,也可以使用该模式来对一些字符串进行匹配,观察这些字符串是否符合该模式,实现字符串的筛选等功能,用途非常广泛

1.1.2 正则表达式的作用

  • 当我们需要需要对字符串进行解析来获取数据时,我们可以通过字符串定位(find)的方法,来寻找到自己所需要的信息或者缩减寻找信息的范围,但是面对情况复杂的字符串信息,可能需要设置多种条件来一一判断;情况越复杂,条件分支就会增长的极快,所以使用常规的字符串定位,然后进行判断的方法显然就不合适了;特别是在网络爬虫爬取的数据分析中,面对大量的字符数据要靠着自己来设置条件判断显然是非常困难的,所以我们需要使用正则表达式来进行字符串的匹配,从而实现我们想要的功能

1.2 正则表达式的使用

1.2.1 前提

(1)选用示例语言

  • 为了方便,本文使用python作为正则表达式的使用示例,其实绝大部分主流语言都会支持正则表达式,使用过程也几乎相同,只是不同语言有着不同的语法表示而已

(2)文本的获取

  • 使用requests库来获取B站首页html页面,将其写入到文本中,避免每次测试都需要动用网络资源

    immport requests
    
    # 将html页面写入到文本
    with open("data.html","w",encoding="utf-8") as html:
        response = requests.get("https://www.bilibili.com/")
        html.write(response.text)
        
    # 读取文本内容
    text = ‘‘
    with open("data.html",‘r‘,encoding="utf-8") as html:
        text = html.read()
    

(3)正则表达式的效果查看

  • 学习过程中可以使用网站:regex101,来查看匹配效果:

    • 上面为正则表达式,下面为文本内容,右侧为匹配的详细信息

    技术图片

1.2.2 使用python匹配正则表达式

(1)re

  • 导入re(regular expression)库

    import re
    

(2)re.Pattern

  • 该类为re库中的一个类,通过传入正则表达式构造该对象,用于去寻找匹配的字符串

    pattern = re.compile(r"正则表达式")
    
  • 通常使用 r"字符串"来表示这是一个原始字符串,即不对一些特殊字符,如""," "等进行转义,一下为两者区别的示例:

    >>> str = "this is a string.
     	and this is the second line."
    >>> print(str)
    this is a string.
            and this is the second line.
    >>> str = r"this is a string.
     	and this is the second line."
    >>> print(str)
    this is a string.
     	and this is the second line.
    

(3)pattern.finall(文本内容)

  • 该方法为pattern对象的方法,用于依据正则表达式在匹配传入文本中的所有内容,将符合规则的字符串存入列表中,最后返回该列表

    re.compile(r"哔哩哔哩")
    strList = patern.findall(text)
    print(strList)
    

    结果:

    <class ‘list‘>
    [‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩 
    哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, 
    ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔
    哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘]
    

1.2.3 正则表达式中的特殊元字符

正则表达式的特殊字符包括:. * + ? [ ] ^ $ { } | ( )

(1)任意字符.

  • . 表示要匹配除了换行符之外的任何单个字符

    • 比如下面:

      技术图片

  • python示例:

    pattern = re.compile(r‘.哩.哩‘)
    strList = pattern.findall(text)
    print(strList)
    

    结果:

    [‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩 
    哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, 
    ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔
    哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘, ‘哔哩哔哩‘] 
    
  • 可以设置字符匹配模式为DOTALL模式来使得.匹配换行符

    content = ‘‘‘
    <div class="el">
            <p class="t1">           
                <span>
                    <a>Python开发工程师</a>
                </span>
            </p>
            <span class="t2">南京</span>
            <span class="t3">1.5-2万/月</span>
    </div>
    <div class="el">
            <p class="t1">
                <span>
                    <a>java开发工程师</a>
                </span>
    		</p>
            <span class="t2">苏州</span>
            <span class="t3">1.5-2/月</span>
    </div>
    ‘‘‘
    
    pattern = re.compile(r‘class="t1">.*?<a>(.*?)</a>‘, re.DOTALL)
    print(pattern.findall())
    [Python开发工程师, java开发工程师]
    

(2)任意个数字符*

  • *表示匹配前面子表达式任意次

    • 比如:

      技术图片

      • 该处*表示匹配前面"哔哩"任意次,即寻找"哔哩"+"哩"*n(n>=0)这种字符串
    • 再比如:

      技术图片

      • 该处.匹配任意字符,*匹配前面任意字符的任意重复字符串,故匹配的字符串为全文
    • 再比如:

      技术图片

      • 该表达式表示匹配"番"+"组"*n (n>=0)这种形式的字符串

(2)存在或多个字符+

  • +表示匹配前面的子表达式1次或者多次,不包括0次

    • 比如:

      技术图片

      • 该表达式表示匹配"番"+"组"*n (n>=1)这种形式的字符串

(3)非确定字符是否存在?

  • ?表示匹配前面的子表达式0或1次

    • 比如:

      技术图片

(4)指定字符出现次数{m,n}

  • {m,n}表示匹配前面的子表达式m至n次,它会以最大匹配次数来进行匹配

  • {m,}表示匹配前面的子表达式至少m次,它会以最大匹配次数来进行匹配

  • {m}表示匹配前面的子表达式m次,它只会匹配m次,即时后面还有可匹配的

    • 比如:

      技术图片

    • 再比如:

      技术图片

    • 再比如:

      技术图片

(5)指定字符范围[]

  • [abcd]表示该字符只要符合为括号中的一项即可

  • [m-n]表示在m至n的范围内都符合该规则

  • 特殊字符在[]中不再表示其特殊含义,但是依旧会有转义作用

    • 比如:

      技术图片

  • 如果在方括号中使用^,表示匹配不是括号中的字符

    • 比如:

      技术图片

(6)开头^

  • 表示匹配文本的起始位置

  • 如果是单行模式 ,表示匹配整个文本的开头位置。

    • 比如:

      text = """PS E:PythonStudy> python -u "e:PythonStudy
      egexp
      eglearn.py"   
      [‘Alice‘, ‘Bob‘]
      PS E:PythonStudy> python -u "e:PythonStudy
      egexp
      eglearn.py"   
      [‘Alice‘, ‘Bob‘, ‘大卫‘]
      PS E:PythonStudy> python -u "e:PythonStudy
      egexp
      eglearn.py"   
      [‘Alice‘, ‘Bob‘]
      """
      pattern = re.compile(r‘^P.*?>‘)
      strList = pattern.findall(text)
      print(strList)
      [‘PS E:PythonStudy>‘]
      

    如果是多行模式 ,表示匹配文本每行的开头位置

    • 比如:

      pattern = re.compile(r‘^P.*>‘,re.MULTILINE)
      strList = pattern.findall(text)
      print(strList)
      [‘PS E:PythonStudy>‘, ‘PS E:PythonStudy>‘, ‘PS E:PythonStudy>‘]
      
      • 通过传入re.MUTILINE或者re.M构建pattern对象,实现多行匹配模式

(7)结尾$

  • 表示匹配文本的结束位置

  • 如果是单行模式,表示匹配整个文本的结尾位置

    • text = """Alice say : <== privateKey
      Bob say : ==> publicKey
      """
      pattern = re.compile(r‘S*$‘)
      strList = pattern.findall(text)
      print(strList)
      [‘publicKey‘, ‘‘, ‘‘]
      
  • 如果是多行模式,表示匹配文本每行的结尾位置

    • pattern = re.compile(r‘S*$‘,re.M)
      strList = pattern.findall(text)
      print(strList)
      [‘privateKey‘, ‘‘, ‘publicKey‘, ‘‘, ‘‘]
      

(8)其中之一|

  • 表示匹配前者或者后者,必须符合其中一个标准

    • 比如

      技术图片

(9)转义

  • 对特殊字符进行转义,用于将特殊字符看做普通字符,参与匹配过程

  • 匹配某种字符类型:

    模式 含义
    w 匹配数字字母下划线等价于[ a-zA-Z0-9 ],缺省情况下也包括Unicode文字字符,可以指定为只包括Ascll字符
    W 匹配非数字字母下划线,等价于[ ^a-zA-Z0-9 ]
    s 匹配任意空白字符,等价于 [ f ](tab,换行,空格)
    S 匹配任意非空字符,等价于[ ^ f]
    d 匹配任意数字,等价于 [0-9]。
    D 匹配任意非数字,等价于[ ^0-9 ]
    A 匹配字符串开始
     匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
    z 匹配字符串结束
    G 匹配最后匹配完成的位置。
     匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er‘ 可以匹配"never" 中的 ‘er‘,但不能匹配 "verb" 中的 ‘er‘。
    B 匹配非单词边界。‘erB‘ 能匹配 "verb" 中的 ‘er‘,但不能匹配 "never" 中的 ‘er‘。
    , , f,v 匹配一个换行符,等价于 x0a 和 cJ;匹配一个制表符, 等价于 x09 和 cI;等;匹配一个换页符,等价于 x0c 和 cL;匹配一个垂直制表符。等价于 x0b 和 cK
    um 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,‘(.)1‘ 匹配两个连续的相同字符

菜鸟教程

  • 对于w,如果不指定风格,则会包含unicode编码字符

    • 比如:

      text = """
      Alice
      Bob
      大卫
      """
      pattern = re.compile(r‘w{2,5}‘)
      strList = pattern.findall(text)
      print(strList)//[‘Alice‘, ‘Bob‘, ‘大卫‘]
      
    • 可以使用指定方式re.A,或则re.ASCII指定匹配模式为字符只包含ascii码的字符

      pattern = re.compile(r‘w{2,5}‘,re.A)
      strList = pattern.findall(text)
      print(strList)//[‘Alice‘, ‘Bob‘]
      

1.2.4 分组

(1)分组介绍

  • 当通过正则表达式寻找匹配字符串时,可以通过加入()的方式来对结果进行分组,从而获取匹配字符串中的有效信息

(2)分组的使用

  • python代码示例:

    text = """
    2020-4-13 22:04:27 Alice : hello,world.
    2020-4-13 22:04:32 Bob : nice to meet you.
    """
    # 使用分组
    pattern = re.compile(r‘(w*)s:s(.*).‘,re.M)
    strList = pattern.findall(text)
    print(strList)
    
    # 不使用分组
    pattern = re.compile(r‘w*s:s.*.‘,re.M)
    strList = pattern.findall(text)
    print(strList)
    

    结果:

    [(‘Alice‘, ‘hello,world‘), (‘Bob‘, ‘nice to meet you‘)]
    [‘Alice : hello,world.‘, ‘Bob : nice to meet you.‘]
    
  • 通过使用()将所需要的信息分离出来,最终会以元素为元组的列表作为返回结果

1.2.5 贪婪模式与非贪婪模式

(1)贪婪模式

  • 贪婪模式是指在匹配时会尽可能的多匹配,当匹配的字符串已经符合要求时,如果后面的字符串依旧符合要求,则会继续进行匹配,知道后面的字符串不符合规则

    • 比如:

      技术图片

      技术图片

      • 在寻找""pic":xxxxx.jpg"时,即时到第3行就已经匹配完成了,在该模式下,依旧会进行匹配,直至找到最后一个.jpg
  • +*都为贪婪模式,会尽可能地去匹配

(2)非贪婪模式

  • 该模式匹配到第一个符合表达式规则的字符串就会停止

  • 通过在+*{}后面加上?来使之以非贪婪模式匹配,只要满足第一次符合规则的情况就可以

    • 比如:

      技术图片

      • 在寻找到第一个符合表达式就停止,输出匹配字符串,再继续检测
    • 再比如:

      技术图片

      • 本来结果应该是111,111,1,加入?之后只要有一个1即符合表达式,所以结果为1,1,1,1,1,1,1

1.3 python正则表达式使用

1.3.1 re函数

(1)re.match(pattern, string, flags=0)

  • 该方法只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None;如果匹配成功返回match对象

  • 通过group方法来获得结果

    text = """http:www.pics.com/cat.jpg
    http:www.imgs.com/dog.png
    http:www.imgs.com/doge.png
    http:www.pics.com/bird.gif
    http:www.pics.com/horse.jpeg
    """
    
    pattern = ‘http.*.(.*).com.*(S*.S*)‘
    print(re.match(pattern, text).group(0)) 
    print(re.match(pattern, text).group(1))  
    print(re.match(pattern, text).group(2)) 
    print(re.match(pattern, text).group()) 
    
    http:www.pics.com/cat.jpg
    pics
    .jpg
    http:www.pics.com/cat.jpg
    

(2)re.search(pattern, string, flags=0)

  • re.search 扫描整个字符串并返回第一个成功的匹配

(3)re.sub(pattern, repl, string, count=0, flags=0)

  • re.sub用于替换字符串中的匹配项

    • repl : 替换的字符串,也可为一个函数
    • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配
  • 该方法返回一个新的被替换的字符串,源字符串不受影响

  • 示例:

    print(re.sub(pattern,"哈哈",text))
    哈哈:www.pics.com/cat.jpg
    哈哈:www.imgs.com/dog.png
    哈哈:www.imgs.com/doge.png
    哈哈:www.pics.com/bird.gif
    哈哈:www.pics.com/horse.jpeg
    

(4)re.compile(pattern[, flags])

  • compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,提供match()和search() 这两个函数使用

  • Pattern对象拥有re中的函数方法,即re.函数的Pattern参数为本身

  • 例:

    pattern = re.compile(r‘pics.*/(S+).*‘)
    
    print(re.sub(pattern,"哈哈",text))
    print(pattern.sub("哈哈",text)) # 两者效果相同
    
  • python源码:

    # 这是re.search方法,其它类似
    def search(pattern, string, flags=0):
        """Scan through string looking for a match to the pattern, returning
        a Match object, or None if no match was found."""
        return _compile(pattern, flags).search(string)
    
    • re.中的匹配函数全部为套娃方法,即先构造Pattern对象,再利用该对象的方法完成其功能

(5)re.findall(string[, pos[, endpos]])

  • 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表

(6)re.finditer(pattern, string, flags=0)

  • 和 findall 类似,在字符串中找到正则表达式所匹配的所有子串的match对象,并把它们作为一个迭代器返回

  • 示例

    pattern = re.compile(r‘pics.*/(S+).*‘)
    for match in pattern.finditer(text):
        print(match.group(1))
        
    cat.jpg
    bird.gif
    horse.jpeg
    

(7)re.split(pattern, string[, maxsplit=0, flags=0])

  • split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下

    pattern = re.compile(r‘/‘)
    print(pattern.split(text))
    
    [‘http:www.pics.com‘, ‘cat.jpg
    http:www.imgs.com‘, ‘dog.png
    http:www.imgs.com‘, ‘doge.png
    http:www.pics.com‘, ‘bird.gif
    http:www.pics.com‘, ‘horse.jpeg
    ‘]
    

1.3.2 python中的匹配模式

(1)python中的正则表达式匹配模式

  • 源码

    class RegexFlag(enum.IntFlag):
        ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"          指定字符集为ascii码
        IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case          忽略大小写
        LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale  
        UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale"   根据Unicode字符集解析字符。这个标志影响 w, W, , B
        MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline  多行匹配
        DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline 使 . 匹配包括换行在内的所有字符
        VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments 这个选项忽略规则表达式中的空白和注释,并允许使用 ’#’ 来引导一个注释。这样可以让你把规则写得更美观些
        A = ASCII
        I = IGNORECASE
        L = LOCALE
        U = UNICODE
        M = MULTILINE
        S = DOTALL
        X = VERBOSE
        # sre extensions (experimental, don‘t rely on these)
        TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
        T = TEMPLATE
        DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
    

以上是关于正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

PHP 正则表达式总结

正则表达式

正则表达式

正则表达式“或“的使用

正则表达式 验证数字格式 非负数 小数点后保留两位 数字正则 double正则

JS正则表达式详解