正则表达式与re模块

正则表达式在线测试工具: http://tool.chinaz.com/regex/
本文大量参考:https://www.cnblogs.com/Eva-J/articles/7228075.html#_label10
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

一、字符组:[字符组]

在同一个位置可能出现的各种的字符组成了一个字符串,在正则表达式中用[ ]表示,字符分为很多类,比如数字、字母、标点等等。
例如你现在要求在一个位置上“只能出现一个数字”,那么这个位置上的字符只能是0、1、2…9这10个数之一。

正则待匹配字符匹配结果说明
[0123456789]8True8出现在字符组内,可以匹配
[0123456789]aFalsea不在在字符组内,不能匹配
[0-9]7True简化写法,用-表示范围
[a-z]sTrue[a-z]表示匹配任意一个小写字母
[0-9a-fA-F]eTrue可以匹配数字,大小写形式的a~f,用来验证十六进制字符
二、元字符
元字符匹配内容
.匹配除换行符一维的任意字符
\w匹配字母或数字或下划线
\s匹配任意的空白字符
\d匹配数字
\W匹配非字母或数字或下划线
\D匹配非数字
\S匹配非空白符
\n匹配一个换行符
\t匹配一个制表符
\b匹配一个单词的结尾
^匹配字符串的开始
$匹配字符串的结尾
ab
()匹配括号内的表达式,也表示一个组
[…]匹配字符组中的字符
^…匹配除了字符组中字符的所有字符
三、量词
量词用法说明
重复零次或一次
+重复一次或者多次
*重复零次或者多次
{n}重复n次
{n,}重复n次货更多次
{n,m}重复n到m次

量词

一些例子

. ^ $

正则待匹配字符匹配结果
小.小杰小海小梅小杰 小海 小梅
^小.小杰小海小梅小杰
小.$小杰小海小梅小梅

* + ? {}

正则待匹配字符匹配结果
小.?小杰和小海龟和小梅大大小杰 小海龟 小梅大大
小.*小杰和小海龟和小梅大大小杰和小海龟和小梅大大
小.+小杰和小海龟和小梅大大小杰和小海龟和小梅大大
小.{1,2}小杰和小海龟和小梅大大小杰和 小海龟 小梅大

注意,前面的 *,+,?等量词都是默认贪婪匹配,量词后面加?可以将其变成惰性匹配。

正则待匹配字符匹配结果
小.&?小杰和小海龟和小梅大大小 小 小

字符集[ ] [ ^ ]

正则待匹配字符匹配结果
小[杰海龟梅大大]*小杰和小海龟和小梅大大小杰 小海龟 小梅大大
小[^和]*小杰和小海龟和小梅大大小杰 小海龟 小梅大大
[\d]520acdh55 2 0 5
[\d]+520acdh5520 5

分组()与 | [^]
身份证号码是一个长度为15或者18个字符的字符串,如果是15位那么全部都由数字组成,首位不能为0;如果是18位,那么前17位全部是数字,末位可能是数字或者x,下面用正则来表示:

正则说明
^[1-9]\d{13,16}[0-9x]$可以匹配一个正确的身份证,但是16、17位的错误身份证也会被匹配
^[1-9]\d{14}{\d{2}[0-9x]}?$完美匹配,()表示分组,整体约束\d{2}[0-9x]出现的次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}
四、转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对""进行转义,变成’\’。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成’\n’,那么正则里就要写成"\\n",这样就太麻烦了。这个时候我们就用到了r’\n’这个概念,此时的正则是r’\n’就可以了。

正则待匹配字符匹配结果说明
\n\nFalse因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配
\\n\nTrue转义\之后变成\,即可匹配
‘\\\\n’‘\\n’True如果在python中,字符串中的’‘也需要转义,所以每一个字符串’'又需要转义一次
r’\\n’r’\n’True在字符串之前加r,让整个字符串不转义
五、贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则待匹配字符匹配结果说明
<.*><script>…<script><script>…<script>默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?><script>…<script><script> <script>加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

几个常用的非贪婪模式搭配
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

六、re模块下的常用方法
import re
'''findall 没有匹配到返回一个空列表,search和match没有匹配到就会报错'''
ret = re.findall('a', 'jacky lu hallo')  # 返回所有满足匹配条件的结果,放在列表里
print(ret)  # 结果 : ['a', 'a']

ret = re.search('a', 'jacky lu hallo').group()
print(ret)  # 结果 : 'a'
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。

ret = re.match('a', 'abc').group()  # 用法和search一样,不过match只能从字符串的开头开始匹配
print(ret)  # 结果 : 'a'

ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret)  # ['', '', 'cd']

ret = re.sub('\d', 'J', '1jacky89ha', 1) # 将数字替换成'H',参数1表示只替换一次
print(ret)  # Jjacky89ha  

ret = re.subn('\d', 'J', '1jacky89ha')  # 将数字替换成'H',会返回一个元祖,(替换的结果,替换了多少次)
print(ret)  # ('JjackyJJha', 3)

compiled_obj = re.compile('\d{2}')  # 将正则表达式编译为一个正则表达式对象,规则要匹配的是2个数字
ret = compiled_obj.search('jacky56haha')  # 正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  # 结果 : 56

import re
ret = re.finditer('\d', 'ja8xasca7856a')   # finditer返回一个存放匹配结果的迭代器
print(ret)  # <callable_iterator object at 0x000001AF202BC2B0>
print(next(ret).group())  # 8  查看第一个结果
print(next(ret).group())  # 7  查看第二个结果
print([i.group() for i in ret])  # ['8', '5', '6']  查看剩余的结果
七、need注意的一些内容
  1. | 从左到右匹配,只是匹配上了就不继续匹配了,应该把长的放在前面。
    [^] 除了字符组内的其他都匹配

  2. findall 的优先级查询

import re
# 使用findall用来匹配含分组的正则表达式时, 会优先把匹配结果组里内容返回
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['oldboy']

# 如果想要匹配结果,分组内前加'?:'取消权限即可
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['www.oldboy.com']
  1. split的优先级查询
import re
# 没有()的没有保留所匹配的项
ret = re.split('\d+', 'jacky7haha78wei')
print(ret)  # ['jacky', 'haha', 'wei']

# 有()的却能够保留了匹配的项,这个在某些需要保留匹配部分的使用过程是非常重要的。
ret = re.split('(\d+)', 'jacky7haha78wei')
print(ret)  # ['jacky', '7', 'haha', '78', 'wei']
八、综合练习与拓展
  1. 匹配标签
import re

ret = re.search(r"<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>hello</h1>")
# 还可以在分组中利用?<name>的形式给分组起名字
# 获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret)  # <_sre.SRE_Match object; span=(0, 14), match='<h1>hello</h1>'>
print(ret.group('tag_name'))  # 结果 :h1
print(ret.group())  # 结果 :<h1>hello</h1>

ret = re.search(r"<(\w+)>\w+</\1>", "<h1>hello</h1>")
# 如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# 获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))  # 结果 :h1
print(ret.group())  # 结果 :<h1>hello</h1>
  1. 匹配整数
import re

ret = re.findall(r"\d+", "1-2*(60+(-40.35/5)-(-4*3))")
print(ret)  # ['1', '2', '60', '40', '35', '5', '4', '3']
ret = re.findall(r"-?\d+\.\d*|(-?\d+)", "1-2*(60+(-40.35/5)-(-4*3))")
print(ret)  # ['1', '-2', '60', '', '5', '-4', '3']
ret.remove("")
print(ret)  # ['1', '-2', '60', '5', '-4', '3']
  1. 数字匹配

if: ^\d{4}-0?[1-9]|1[0-2]$

1 匹配一段文本中的每行的邮箱
https://blog.csdn.net/qq_41823444/article/details/100540347

2 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’

分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3 匹配qq号。(腾讯QQ号从10000开始) [1,9][0,9]{4,}

4 匹配一个浮点数。 ^(-?\d+)(.\d+)?$ 或者 -?\d+.?\d*

5 匹配汉字。 ^[\u4e00-\u9fa5]{0,}$

6 匹配出所有整数

  1. 爬虫练习
import requests

import re
import json

def getPage(url):

    response=requests.get(url)
    return response.text

def parsePage(s):
    
    com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
                   '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',re.S)

    ret=com.finditer(s)
    for i in ret:
        yield {
            "id":i.group("id"),
            "title":i.group("title"),
            "rating_num":i.group("rating_num"),
            "comment_num":i.group("comment_num"),
        }

def main(num):

    url='https://movie.douban.com/top250?start=%s&filter='%num
    response_html=getPage(url)
    ret=parsePage(response_html)
    print(ret)
    f=open("move_info7","a",encoding="utf8")

    for obj in ret:
        print(obj)
        data=json.dumps(obj,ensure_ascii=False)
        f.write(data+"\n")

if __name__ == '__main__':
    count=0
    for i in range(10):
        main(count)
        count+=25

简化版

import re
import json
from urllib.request import urlopen

def getPage(url):
    response = urlopen(url)
    return response.read().decode('utf-8')

def parsePage(s):
    com = re.compile(
        '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
        '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)

    ret = com.finditer(s)
    for i in ret:
        yield {
            "id": i.group("id"),
            "title": i.group("title"),
            "rating_num": i.group("rating_num"),
            "comment_num": i.group("comment_num"),
        }


def main(num):
    url = 'https://movie.douban.com/top250?start=%s&filter=' % num
    response_html = getPage(url)
    ret = parsePage(response_html)
    print(ret)
    f = open("move_info7", "a", encoding="utf8")

    for obj in ret:
        print(obj)
        data = str(obj)
        f.write(data + "\n")

count = 0
for i in range(10):
    main(count)
    count += 25

简化版
flags

flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

作业
实现能计算类似
1 - 2 * ( (60-30 +(-40/5) * (9-25/3 + 7 /399/42998 +10 * 568/14 )) - (-43)/ (16-3*2) )等类似公式的计算器程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值