python re模块和正则表达式


Posted in Python onMarch 24, 2021

一、re模块和正则表达式

先来看一个例子:https://reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/

这是京东的注册页面,打开页面我们就看到这些要求输入个人信息的提示。假如我们随意的在手机号码这一栏输入一个11111111111,它会提示我们格式有误。这个功能是怎么实现的呢?假如现在你用python写一段代码,类似:

phone_number = input('please input your phone number : ')

你怎么判断这个phone_number是合法的呢?

根据手机号码一共11位并且是只以13、14、15、18开头的数字这些特点,我们用python写了如下代码:

# 方式一 判断号码是否合法
# -*- coding:utf-8 -*-
while True:
  phone_number = str(input('please input your phone number : '))
  if len(phone_number) == 11 \
      and phone_number.isdigit() \
      and (phone_number.startswith('13') \
         or phone_number.startswith('14') \
         or phone_number.startswith('15') \
         or phone_number.startswith('18')):
    print('是合法的手机号码')
  else:
    print('不是合法的手机号码')

正则表达式不仅在python领域,在整个编程届都占有举足轻重的地位。

不管以后你是不是去做python开发,只要你是一个程序员就应该了解正则表达式的基本使用。如果未来你要在爬虫领域发展,你就更应该好好学习这方面的知识。

但是你要知道,re模块本质上和正则表达式没有关系。re模块和正则表达式的关系 类似于time模块和时间的关系。
时间有自己的格式,年月日时分秒,12个月,365天......已经成为了一种规则。
正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则。
官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

二、正则表达式

在线测试工具 http://tool.chinaz.com/regex/

首先谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每一个字都是一个字符串。
其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
比如你要用"1"去匹配"1",或者用"2"去匹配"2",直接就可以匹配上。这连python的字符串操作都可以轻松做到。
那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。

2.1 字符组

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

正则 待匹配字符 匹配
结果
说明
[0123456789] 8 True 在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
[0123456789] a False 由于字符组中没有"a"字符,所以不能匹配
[0-9] 7 True 也可以用-表示范围,[0-9]就和[0123456789]是一个意思
[a-z] s True 同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
[A-Z] B True [A-Z]就表示所有的大写字母
[0-9a-fA-F] e True 可以匹配数字,大小写形式的a~f,用来验证十六进制字符

2.2 元字符

元字符 匹配内容
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
\n 匹配一个换行符
\t 匹配一个制表符
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W 匹配非字母或数字或下划线
\D 匹配非数字
\S 匹配非空白符
a|b 匹配字符a或字符b
() 匹配括号内的表达式,也表示一个组
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中字符的所有字符

2.3 量词

量词 用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

2.3.1 . ^ $

正则 待匹配字符 匹配
结果
说明
小. 小明小军小兰 小明小军小兰 匹配所有"小."的字符
^小. 小明小军小兰 小明 只从开头匹配"小."
小.$ 小明小军小兰 小兰 只匹配结尾的"小.$"

2.3.2 * + ? { }

正则 待匹配字符 匹配
结果
说明
小.? 小明和小李子和小巧玲珑

小明
小李
小巧

?表示重复零次或一次,即只匹配"小"后面一个任意字符
小.* 小明和小李子和小巧玲珑 小明和小李子和小巧玲珑 *表示重复零次或多次,即匹配"小"后面0个或多个任意字符
小.+ 小明和小李子和小巧玲珑 小明和小李子和小巧玲珑 +表示重复一次或多次,即只匹配"小"后面1个或多个任意字符
小.{1,2} 小明和小李子和小巧玲珑

小明和
小李子
小巧玲

{1,2}匹配1到2次任意字符

注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

正则 待匹配字符 匹配
结果
说明
小.*? 小明和小李子和小巧玲珑

惰性匹配

2.4 字符集[][^]

正则 待匹配字符 匹配
结果
说明
小[明李子巧玲珑]* 小明和小李子和小巧玲珑

小明
小李子
小巧玲珑

表示匹配"小"字后面[明李子巧玲珑]的字符任意次
小[^和]* 小明和小李子和小巧玲珑

小明
小李子
小巧玲珑

表示匹配一个不是"和"的字符任意次
[\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

2.5 分组 ()与 或 |[^]

身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部由数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不
是一个正确的身份证号码,它是一个
16位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\d{2}[0-9x]分成
一组,就可以整体约束他们出现的
次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\d{16}[0-9x]
如果没有匹配上就匹配
[1-9]\d{14}

2.6 转义符 \

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

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。

这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。

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

2.7 贪婪匹配

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

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script> 默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r'\d'

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

几个常用的非贪婪匹配

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

.*?的用法

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

三、re模块

import re
 
ret = re.findall('a', 'ea eg an') # 返回所有满足匹配条件的结果,放在列表里
print(ret) #结果 : ['a', 'a']
 
ret = re.search('a', 'va eg an').group()
print(ret) #结果 : 'a'
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
 
ret = re.match('a', 'abc').group() # 同search,不过尽在字符串开始处进行匹配
print(ret)
#结果 : 'a'
 
ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret) # ['', '', 'cd']
 
ret = re.sub('\d', 'H', 'va3eg4an4', 1)#将数字替换成'H',参数1表示只替换1个
print(ret) #vaHeg4an4
 
ret = re.subn('\d', 'H', 'va3eg4an4')#将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret)
 
obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group()) #结果 : 123
 
import re
ret = re.finditer('\d', 'ds3sy4784a')  #finditer返回一个存放匹配结果的迭代器
print(ret) # <callable_iterator object at 0x10195f940>
print(next(ret).group()) #查看第一个结果
print(next(ret).group()) #查看第二个结果
print([i.group() for i in ret]) #查看剩余的左右结果

注意:

1 findall的优先级查询:

import re
 
ret = re.findall('www.(baidu|xunlei).com', 'www.xunlei.com')
print(ret) # ['xunlei']   这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
 
ret = re.findall('www.(?:baidu|xunlei).com', 'www.xunlei.com')
print(ret) # ['www.xunlei.com']

2 split的优先级查询

ret=re.split("\d+","va3eg4an")
print(ret) #结果 : ['va', 'eg', 'an']
 
ret=re.split("(\d+)","va3eg4an")
print(ret) #结果 : ['va', '3', 'eg', '4', 'an']
 
#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。

3.1 匹配标签

import re
 
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?<name>的形式给分组起名字
#获取的匹配结果可以直接用group('名字')拿到对应的值
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))
print(ret.group()) #结果 :<h1>hello</h1>

2、匹配整数

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']

3、数字匹配

1、 匹配一段文本中的每行的邮箱
http://blog.csdn.net/make164492212/article/details/51656638

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、 匹配出所有整数

4、爬虫

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有很多可选值:

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-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式的计算器程序

Python 相关文章推荐
python连接mysql数据库示例(做增删改操作)
Dec 31 Python
python求斐波那契数列示例分享
Feb 14 Python
matplotlib设置legend图例代码示例
Dec 19 Python
python绘制多个曲线的折线图
Mar 23 Python
python使用pygame框架实现推箱子游戏
Nov 20 Python
使用python绘制3维正态分布图的方法
Dec 29 Python
使用Python批量修改文件名的代码实例
Jan 24 Python
超简单使用Python换脸实例
Mar 27 Python
Python 实现使用空值进行赋值 None
Mar 12 Python
Python selenium爬虫实现定时任务过程解析
Jun 08 Python
Python unittest discover批量执行代码实例
Sep 08 Python
Python通过yagmail实现发送邮件代码解析
Oct 27 Python
opencv实现图像几何变换
PyQt QMainWindow的使用示例
Mar 24 #Python
PyQt 如何创建自定义QWidget
Mar 24 #Python
解决python 输出到csv 出现多空行的情况
opencv实现图像平移效果
python+selenium小米商城红米K40手机自动抢购的示例代码
Python使用openpyxl复制整张sheet
Mar 24 #Python
You might like
咖啡豆的最常见发酵处理方法,详细了解一下
2021/03/03 冲泡冲煮
PHP动态页生成静态页的3种常用方法
2014/11/13 PHP
php截取指定2个字符之间字符串的方法
2015/04/15 PHP
PHP抽奖算法程序代码分享
2015/10/08 PHP
PHP sleep()函数, usleep()函数
2016/08/25 PHP
确保Laravel网站不会被嵌入到其他站点中的方法
2019/10/18 PHP
PHP获取php,mysql,apche的版本信息及更多服务器信息
2021/03/09 PHP
新老版本juqery获取radio对象的方法
2010/03/01 Javascript
HTML复选框和单选框 checkbox和radio事件介绍
2012/12/12 Javascript
JS实现控制表格行文本对齐的方法
2015/03/30 Javascript
利用jQuery实现漂亮的圆形进度条倒计时插件
2015/09/30 Javascript
jQuery插件Easyui设置datagrid的pageNumber导致两次请求问题的解决方法
2016/08/06 Javascript
使用ngrok+express解决本地环境中微信接口调试问题
2018/02/26 Javascript
微信小程序日历/日期选择插件使用方法详解
2018/12/28 Javascript
详解Vue组件之间通信的七种方式
2019/04/14 Javascript
微信端调取相册和摄像头功能,实现图片上传,并上传到服务器
2019/05/16 Javascript
es6中class类静态方法,静态属性,实例属性,实例方法的理解与应用分析
2020/02/15 Javascript
[01:14:10]2014 DOTA2国际邀请赛中国区预选赛 SPD-GAMING VS Orenda
2014/05/22 DOTA
[01:04:31]DOTA2-DPC中国联赛定级赛 iG vs Magma BO3第二场 1月8日
2021/03/11 DOTA
python测试mysql写入性能完整实例
2018/01/18 Python
Python实现查找数组中任意第k大的数字算法示例
2019/01/23 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
Python3.7安装pyaudio教程解析
2020/07/24 Python
详解Anaconda安装tensorflow报错问题解决方法
2020/11/01 Python
澳大利亚领先的运动鞋商店:Hype DC
2018/03/31 全球购物
瑞典最大的儿童用品网上商店:pinkorblue.se
2021/03/09 全球购物
北京-环亚运商测试题.net程序员初步测试题
2013/05/28 面试题
某公司部分笔试题
2013/11/05 面试题
黄金酒广告词
2014/03/21 职场文书
园林技术专业求职信
2014/07/28 职场文书
学校开学标语
2014/10/06 职场文书
2015年小学中秋节活动总结
2015/03/23 职场文书
工厂仓库管理员岗位职责
2015/04/09 职场文书
敬老院活动感想
2015/08/07 职场文书
工作总结之小学教师体育工作范文(3篇)
2019/10/07 职场文书
JavaScript 反射学习技巧
2021/10/16 Javascript