一,Python自动化管理

1,使用python实现邮件自动化发送

1. 发送纯文件邮件

邮件是非常自由且通用的联系方式之一,不用及时回复,记录可以保存,有空查看和回复即可。

使用Python发送邮件,也是非常便利,常用于程序的运行结果上报等...

本节课程,学习使用Python发送纯文字邮件。

首先介绍一下所使用的库:yagmail,安装命令:pip install yagmail

虽然Python官方内置了邮件相关库,但是使用起来,不方便理解,且代码量很大。所以使用yagmail,简单、易用、容易书写和管理。

使用的邮箱是QQ邮箱,QQ账号可以自动申请一个邮箱,所以现在,大家都应该至少有一个QQ邮箱了。

授权码,就是一会代码中的密码,账号直接是邮箱。

代码部分【密码部分已隐藏】,首先导入库,然后初始化对象,如下:

import yagmail
yag = yagmail.SMTP(user='[email protected]', password='ycw********bfff',host='smtp.qq.com')

然后发送邮件,目标是自己,随便写一个标题和内容,如下:

yag.send(to = '[email protected]',subject ='测试邮件的标题',contents = "测试邮件的内容")

代码的运行,不能出报错。

执行完成后,去QQ邮箱页面,刷新页面,看下有没有收到邮件,长时间没收到,也是失败。

2,发送网页格式邮件

邮件的内容,可以是简单的几个文字,也可以是丰富的HTML页面。

但是刚上手是无法做出精美的HTML网页邮件的,先来做个基础的HTML格式邮件。

首先同样是将yagmail初始化成对象,如下代码:

import yagmail
yag = yagmail.SMTP(user='[email protected]', password='****************',host='smtp.qq.com')

依旧是使用qq邮箱,密码记得获取授权码

然后是准备subject,以及内容和html内容,如下:

subject = '测试邮件的标题【无HTML】'
body = '测试邮件的内容部分,看下方,看下方,看下方'

yag.send(to = '[email protected]',subject =subject,contents = body)

先发送一个没有HTML格式的文字邮件。

然后再发送一个含有HTML的邮件,如下代码:

subject = '测试邮件的标题【有HTML】'
body = '测试邮件的内容部分,看下方,看下方,看下方'
h1 = '<h1>Spbeen</h1>'
a_link = '点击<a href="http://www.spbeen.com">链接</a>,前往Spbeen网站'

yag.send(to = '[email protected]',subject =subject,contents = [body,h1,a_link])

3,发送携带附件的文件

以往的邮件发送形式,通常都会带上附件,例如工作中的文件发给领导、将小组作业发给老师等等

Python中使用yagmail发送含附件的邮件,非常的简单,且方便。

首先是准备代码部分:

import yagmail
yag = yagmail.SMTP(user='[email protected]', password='****************',host='smtp.qq.com')

发送附件,就要准备附件文件。源码文件夹中准备了三个文件,分别是"GCD.py"、"python.png"、"django.png"。

先发第一个邮件,放上py文件,代码如下:

subject = '测试邮件的标题'
body = '测试邮件的内容部分,看下方,看下方,看下方'

h1 = '<h1>Spbeen</h1>'
a_link = '点击<a href="http://www.spbeen.com">链接</a>,前往Spbeen网站'
py_source = "GCD.py"

yag.send(to = '[email protected]',subject =subject,contents = [body,h1,a_link,py_source])

发送邮件时增加附件,只需要在contents列表中,放上文件的路径。这里放的是py_source,也就是“GCD.py”名称,文件和发送邮件的ipynb文件放在一起。

接着发送多个附件,除了py文件,将两个png图片也一并发送出去,如下代码:

subject = '测试邮件的标题'
body = '测试邮件的内容部分,看下方,看下方,看下方'

h1 = '<h1>Spbeen</h1>'
a_link = '点击<a href="http://www.spbeen.com">链接</a>,前往Spbeen网站'

py_source = "GCD.py"
django_img = "django.png"
python3_img = "python3.png"

yag.send(to = '[email protected]',subject =subject,contents = [body,h1,a_link,py_source,django_img, python3_img])

发送多个附件的邮件,只需要将文件的路径,直接放到contents列表中去即可,代码和上面的单附件代码一致

4,管理邮件的接收

既然可以发送邮件,就一定可以接收邮件,这两者是相互的。

接收邮件,这里介绍zmail库,安装命令:pip install zmail

zmail支持邮件的发送和接收,操作的类型是字典。

接收邮件,同样是先初始化,如下代码:

import zmail
server = zmail.server('[email protected]','*************')

这里的server类似于一个邮箱客户端,可以通过server来取邮箱中收到的邮件,例如:

mail = server.get_latest()

这是获取邮箱中最后一个邮件,也就是最新的邮件。

2,Python做一个属于自己的web网站

1,掌握前端技术开发精髓

网页内容,由三部分组成,分别是html、css和javascript。

html是网页面部分,css是美化网页的操作,javascript是让网页可以交互起来。

前端内容,html是必须的,css可以交给前端框架,js也可以交给框架。

所以本节课这里主要介绍html部分和认识一个前端框架。

html是一种标记语言,结构是这样的的内容,一个尖括号的起始标签,一个尖括号带 / 的结束标签。

并且这种成对的标签,是可以嵌套的。

以下是html语法规则,下面来认识下基础html结构。

<!DOCTYPE html>
<html>
    <head>
        <title>标题</title>
    </head>
    <body>
    内容
    </body>
</html>

代码解释:

  • 顶部第一行是申明,现在的HTML已经到了第五代。
  • 然后html标签是开始,所有内容全部放在html标签里面。
  • html内部只有head和body标签两个,也叫作网页的头和身体部分头部用于存放网页的说明,例如title、meta等标签身体部分放网页内容,就是你在浏览器中看到的内容
  • title是特定的标签,就是这个网页的名字,展示在浏览器的标签页部分的信息

以上是html的基础结构,接下来了解下前端框架,这里要介绍的是Bootstrap3。

Bootstrap3是非常著名的前端框架之一,也是最早的前端框架。

一个小疑问:为什么会有前端框架的出现?做网站,费时费力,网站难的是后台的服务搭建,这部分是用户看不到的,用户看到的地方是网页内容。及时你的后台在强大,前端效果不好看,用户也会觉得你这个站很low。但并不是所有的人都懂如何美化网页,所有一个现成的前端框架就非常有必要的。使用前端框架,可以快速的实现你要的内容,并且配合上具体的使用文档,及时没有网页开发经验,也可以按照说明,快速的完成一个还算美观的前端页面。

关于Bootstrap3的官方中文文档,官方链接:Bootstrap3官方中文

### 2,Django的安装和基础使用

Python做web开发,非常的方便和快捷,这个优势,得益于Python的两个框架,一个Flask,一个Django。

Flask小,微框架,只含有核心组件,其他的内容,都需要找库或者自行开发,适用进阶学习,以及高手使用,完全按着自己的思路,来使用flask搭建网站。

Django大而全,开发迅速,组件完整,可以快速的搭建一个站起来,但是必须要安装Django的思路来搭建,所以适合新手学习。

本节课程来学习Django框架的安装和使用。

首先是框架的安装,安装命令:pip install django==2.2

目前django已经出到了3版本,但是不推荐学习,django3的目标是接入异步,就目前源码来看,并不是非常好用。所以从学习的角度来看,推荐django2.2版本。

安装好之后,使用命令提示符或者终端,就可以使用django-admin命令,来创建项目,如下效果图:

django-admin startproject first   #first是项目名

项目新建成功,下面用你顺手的编辑器打开,这里推荐pycharm、vscode、sublime,都可以。

项目对应站,应用对应站的很多功能功能,所以app应用可以创建很多个。

创建app的命令是:python manage.py startapp testweb

最后的testweb是app名称。

创建好之后,打开first目录中的settings.py,找到INSTALLED_APPS,将app名称,也就是testweb放进去。
testweb.png

到此,代码就全部准备好了,接下来是启动。

启动命令是:python manage.py runserver,如下效果图:

出现了127.0.0.1:8000就说明启动成功了,此时可以访问了,且终端下方,无法输入内容,也就是网站运行中。

下面是访问网站页面的效果图:

image-20200423182822097.png

Django大版本之后有语法差别,小版本之间没什么差别,都是一些细节上的优化和改动,所以你安装了django2,就可以正常的学习本课程。

3,理解MTV模型

要掌握Django,必须了解Django的MTV模型,这是非常重要的内容,不管是Django1 还是2 以及后面正在更新的3,MTV模型是永远不会变的。

先来解释下MTV模型的意思,MTV是三层关系,分别是:

  • M Model 模型,数据管理
  • T Template 模板,网页展示
  • V View 视图,逻辑控制

这三者是Django的网页的运行机制,下面单独介绍下MTV每个模板的功能。

Model模型介绍

Model模型,是负责管理数据的。
每个网站都需要有数据库用于存储网站数据,网站需要展示数据时,也需要从数据库查询并读取数据。
Django内置了ORM实现框架,支持多种数据库,默认的数据库是Sqlite,当然也支持Mysql等关系型数据库。
Template介绍

Template模板,指的就是网页模板。

真实给用户看的内容,都是数据+网页模板的结合。

数据从数据库中查询出来,并渲染到模板中,得到单个的网页,再把网页返回给用户查看,这就是网页的渲染流程。

那模板长什么样子?

上节课程最好的静态网页,放到django项目的特定文件夹中,就是我们的模板,再简单的稍作修改,就得到了可以渲染数据的模板。

View视图介绍

前面介绍了各种操作,例如查询并读取数据、数据渲染到网页,虽然有介绍,但并未介绍如何写,怎么写,写在哪。

这些逻辑代码,都是要写在view视图中的。

View视图,就是对应的逻辑代码存放的位置。

网站是要接收用户的请求,并返回给对应的响应,而django接受到的请求,发给指定的视图函数,视图函数做设定好的操作,再返回响应给用户,这样就完成了一次请求和响应操作。

4,制作首页

放入静态文件

django项目准备好了,静态网页也准备好了,本节课学习如何将网页接入到项目中。

第一步,在testweb应用的目录下,创建一个名为templates的目录,注意不要写错。

这个templates目录,就是存放模板的目录,名称不能错,因为这是django默认读取的目录。

然后将两个html文件放进来。

写一个函数

要完成网站首页,就必须定义首页的视图函数,这个函数放在testweb目录下的views.py文件中。

打开了views.py文件,新建一个index函数,参数是request,如下代码:

def index(request):
    return render(request, 'index.html')

这个就是视图函数的定义,代码解释:

  • 第一行的index是函数名,request是参数,必须要,因为函数在被调用时,请求会被传入,request就是对应的参数。
  • 第二行return,这个是返回,函数必须有返回
  • render是django内置的渲染函数,这里放入了两个值第一个是request,默认的,需要加;第二个值是'index.html',这个是放在templates目录中的首页
  • render()函数的结果是一个Response响应,每个视图函数都必须返回一个响应。

以上是函数的介绍部分,那这个函数也就是简单的返回了一个载入index.html的响应。

准备URL

函数有了,那就给他分配一个url,url就是浏览器中访问的地址。

url的配置,在first目录下的urls.py文件中,这里有一个admin,是默认的django后台配置,后面我们会用到这个。

既然是绑定首页,所以url的地址,应该是主域名,那在绑定的时候,代码如下:

from django.contrib import admin
from django.urls import path
from testweb.views import index
urlpatterns = [
    path('admin/', admin.site.urls)
    path('',index)
]

代码已经准备完毕,现在来启动项目。

打开命令行,输入命令:python manage.py runserver

如果你要绑定到index路径上,只需要修改一下urls.py中的内容就可以。

通常首页,是空url的默认路径,或者有index的url地址,都是可以的。

3,文件自动化处理

1,python中使用正则

本节课的主要内容,在Python中使用正则表达式,方便后续的正则表达式语法学习和实战案例。

首先Python中使用正则表达式,是使用的re库,这是Python自带的库,不需要安装,直接import导入即可

第一步,导入:

import re

函数说明

re是使用正则表达式的库,具体使用方式是用re里面的函数,这里只需要用到,最常用的findall函数,如下:

help(re.findall)
#Help on function findall in module re:
#findall(pattern, string, flags=0)
#    Return a list of all non-overlapping matches in the string. 
#    If one or more capturing groups are present in the pattern, return
#    a list of groups; this will be a list of tuples if the pattern
#    has more than one group.    
#    Empty matches are included in the result.

help是python的内置函数,用于输出help括号内放着的注释信息。

这里的介绍说findall函数,需要三个参数,第一个是pattern正则规则,第二个是string字符串,第三个是flags标记,有默认值0。

也就是使用findall函数,至少需要两个参数,正则规则和字符串,第三个可以不给。

抓取数字

所以现在准备字符串,如下:

re_str = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"

这里定义了一个字符串,参数名是re_str,用于测试re的字符串,内容是将键盘滚一遍,全部数字+小写字母+大写字母。

然后是正则表达式规则,上节课程有表格,介绍了基础的,这里用数字来做第一次测试,如下:

re.findall('\d',re_str)
# ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']

findall给了两个参数,一个是d,在正则表达式中的含义是数字;另一个是re_str,就是需要提取的目标字符串。

得到的结果是['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],就是字符串中的十个数字。

提取字母

现在来测试一下字母,例如提取目标字符串的纯小写字母,如下代码:

re.findall('[a-z]',re_str)
# ['q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c','v','b','n','m']

这里写的正则规则,是[a-z],在正则中的含义就是提取从a到z的任意一个字母,含a和z。

所以得到的结果,是纯小写字母的列表。

提取数字

数字,是正则中最常用的,没有之一,也是后续的实践中用的最多的。

首先准备一个不规则字符串,导入正则库,然后匹配出字符串中的所有数字,如下代码:

import re
target_str = "a1b2c3d4e5f6g7"
re.findall('\d',target_str)
# ['1', '2', '3', '4', '5', '6', '7']

在正则表达式中,d就是数字的意思,所以这里可以拿到目标字符串中的所有数字。

稍微处理下,就可以提取出任意字符串中的所有数字,并转换成int类型,如下代码:

target_str = "a1b2c3d4e5f6g7"
num_list = re.findall('\d',target_str)
nums = int("".join(num_list))
print(nums)
# 1234567

这里匹配的结果,都是单个的数字。使用正则提取多个数字以及特定数字,也是非常方便的

提取多个数字

例如先来抓取多个数字,如下代码:

target_str = "a123b456c7d89e0f00"
print(re.findall('\d',target_str))
print(re.findall('\d+',target_str))
# ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '0', '0']
# ['123', '456', '7', '89', '0', '00']

d在正则中代表数字,准确来说是一个数字。

要提取一个或者一个以上数据,可以使用+号,这个代表一个或一个以上。

指定字符串长度

如果是要特定长度的数字,就是用大括号{},如下代码

target_str = "a123b456c7d89e0f00"
print(re.findall('\d{1}',target_str))
print(re.findall('\d{1,}',target_str))
print(re.findall('\d{1,2}',target_str))
print(re.findall('\d{3,}',target_str))
# ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '0', '0']
# ['123', '456', '7', '89', '0', '00']
# ['12', '3', '45', '6', '7', '89', '0', '00']
# ['123', '456']

大括号{}代表次数,所以每行代码的解释:

  • d{1}就是一个数字,等同于d;
  • d{1,}表示一个或多个,等同于d+;
  • d{1,2}表示一个数字或者两个数字;
  • d{3,}表示三个数字或者更多数字;

这是对数字个数的限制,当然还有具体值的一个限制,例如数字3-7。

选择数字范围

继续往下学习,选取特定的数字范围,如下代码:

target_str = "a123b456c7d89e0f00"
print(re.findall('\d',target_str))
print(re.findall('[3-7]{2}',target_str))
print(re.findall('[3-7]{1,3}',target_str))
# ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '0', '0']
# ['45']
# ['3', '456', '7']

第一条,依旧是匹配所有的数字,注意是一个,这个很简单。

第二条,[3-7]{2}表示获取3到7之间的数字,包含3和7,且匹配两个数字,结果是45,为什么没有3、56和7 ?

这里重点说明下:

  • 要求是两个数字,所以单独的3不行,23是两个,但是2不是范围内
  • 7不行,因为要两个数字
  • 由于第一个就匹配到了45,所以下次匹配开始的路径,是6,而不是56,所以56不会出现。

第三条,规则是[3-7]{1,3},范围是3 4 5 6 7,数字长度可以是1个、2个、3个,所以匹配的结果,3、456和7,这样就拿到了全部。

提取字母

正则中,数字可以用d表示,也可以是[0-9]。字母没有直接的符号表示,可以用[a-z]来固定范围。

下面来提取数字+字母的组合,如下:

target_str = "a123b456c7d89e0f00"
print(re.findall('[a-z]\d{3}',target_str)) # 找出 字母+三个数字的组合
print(re.findall('[a-z]\d{1,}',target_str)) # 找出 字母+一个以上数字的组合
# ['a123', 'b456']
# ['a123', 'b456', 'c7', 'd89', 'e0', 'f00']

如果你看懂了前面一个,这里还是很简单的。

[a-z]是提取小写的26个字母,d{3}代表三个数字,所以匹配的结果,就是['a123', 'b456']

第二条规则,[a-z]d{1,}是获取字母+一个或多个数字的组合,结果是['a123', 'b456', 'c7', 'd89', 'e0', 'f00'],比较多样.

d和D的区别

d代表数字,D代表非数字,在这个示例里面,就可以直接使用D提取字母,如下代码:

target_str = "a123b456c7d89e0f00"
print(re.findall('\D',target_str))
# ['a', 'b', 'c', 'd', 'e', 'f']

大S和小s

有数字和非数字,没有字母,但是有可见字符和不可见字符,如下代码:

target_str = "a123b456c7d89e0f00\n\txyz"
print(re.findall('\S',target_str))
print(re.findall('\s',target_str))
# ['a', '1', '2', '3', 'b', '4', '5', '6', 'c', '7', 'd', '8', '9', 'e', '0', 'f', '0', '0', 'x', 'y', 'z']
# ['\n', '\t']

可见字符,就是我们常见的字母+数字+符号,符号有[email protected]#$%^&*()_+":{}}>>>?<"等。

不可见字符,就是看不见的,但是又需要的内容,例如换行符、制表符、空格符等,存在的目的是为了编排内容,更直观。

n是换行,t是制表符,回车键 "r", 响铃 "007",字符串结束符 "0"。

第一条规则,S,获取所有的可见字符,所以数字和字母都有;

第二条规则,不可见字符,获取的结果是 ['n', 't']。

贪婪模式和非贪婪模式

正则中,有个非常重要的操作,就是贪婪和非贪婪模式,先上代码,如下:

target_str = "1a7j9k4x8n3c0k4n5x8j1a8k4m9"
print(re.findall('\d.*?\d',target_str))
print(re.findall('\d.*\d',target_str))
# ['1a7', '9k4', '8n3', '0k4', '5x8', '1a8', '4m9']
# ['1a7j9k4x8n3c0k4n5x8j1a8k4m9']

贪婪模式是.,非贪婪是 .?。从字面行理解,贪婪就是尽可能的多,非贪婪就是尽可能的少。

.在正则中是表示匹配除n和r外的单个字符,*代表任意次,?表示非贪婪。

先看第二条,.放一起,代表匹配除n和r外的任意字符任意次,所以d.d索要匹配的内容,是数字开头,数字结尾,中间尽可能多的内容。得到的结果是 ['1a7j9k4x8n3c0k4n5x8j1a8k4m9']

相反,第一条规则中,非贪婪模式下,也是数字开头,数字结尾,中间尽可能的少,所以结果是['1a7', '9k4', '8n3', '0k4', '5x8', '1a8', '4m9']

元字符

下面介绍下正则表达式的元字符:

元字符描述
\将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“n”匹配n。“n”匹配换行符。序列“”匹配“”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
^匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“n”或“r”之后的位置。
$匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配“n”或“r”之前的位置。
*匹配前面的子表达式任意次。例如,zo能匹配“z”,也能匹配“zo”以及“zoo”。等价于{0,}。
+匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等价于{0,1}。
{n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m}mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
?当该字符紧跟在任何一个其他限制符(,+,?,{n},{n,},{n,m*})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']
.点匹配除“n”和"r"之外的任何单个字符。要匹配包括“n”和"r"在内的任何字符,请使用像“[sS]”的模式。
(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。
(?:pattern)非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(\)”来组合一个模式的各个部分时很有用。例如“industr(?:y\ies)”就是一个比“industry\industries”更简略的表达式。
(?=pattern)非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,“Windows(?=95\98\NT\2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95\98\NT\2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。
(?<=pattern)非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95\98\NT\2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
(?<!patte_n)非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95\98\NT\2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
x\y匹配x或y。例如,“z\food”能匹配“z”或“food”(此处请谨慎)。“[z\f]ood”则匹配“zood”或“food”。
[xyz]字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
1负值字符集合。匹配未包含的任意字符。例如,“abc”可以匹配“plain”中的“plin”任一字符。
[a-z]字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.
2负值字符范围。匹配任何不在指定范围内的任意字符。例如,“a-z”可以匹配任何不在“a”到“z”范围内的任意字符。
b匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的b就是匹配位置的)。例如,“erb”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“b1”可以匹配“1_23”中的“1”,但不能匹配“21_3”中的“1_”。
B匹配非单词边界。“erB”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
cx匹配由x指明的控制字符。例如,cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
d匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
D匹配一个非数字字符。等价于0-9。grep要加上-P,perl正则支持
f匹配一个换页符。等价于x0c和cL。
n匹配一个换行符。等价于x0a和cJ。
r匹配一个回车符。等价于x0d和cM。
s匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ fnrtv]。
S匹配任何可见字符。等价于 fnrtv。
t匹配一个制表符。等价于x09和cI。
v匹配一个垂直制表符。等价于x0b和cK。
w匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。
W匹配任何非单词字符。等价于“A-Za-z0-9_”。
xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“x41”匹配“A”。“x041”则等价于“x04&1”。正则表达式中可以使用ASCII编码。
num匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)1”匹配两个连续的相同字符。
n标识一个八进制转义值或一个向后引用。如果n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
nm标识一个八进制转义值或一个向后引用。如果nm之前至少有nm个获得子表达式,则nm为向后引用。如果*nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若nm均为八进制数字(0-7),则nm将匹配八进制转义值nm*。
nml如果n为八进制数字(0-7),且ml均为八进制数字(0-7),则匹配八进制转义值nml
un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,u00A9匹配版权符号(©)。
p{P}小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。中括号内的“P”表示Unicode 字符集七个字符属性之一:标点字符。其他六个属性:L:字母;M:标记符号(一般不会单独出现);Z:分隔符(比如空格、换行等);S:符号(比如数学符号、货币符号等);N:数字(比如阿拉伯数字、罗马数字等);C:其他字符。*注:此语法部分语言不支持,例:javascript。
<>匹配词(word)的开始(<)和结束(>)。例如正则表达式能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。
( )将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 1 到9 的符号来引用。
\ 将两个匹配条件进行逻辑“或”(or)运算。例如正则表达式(him\her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。

这些不用刻意去背,需要的时候查阅下即可。

b边界

target_str = "2r1never"
print(re.findall(r'er\b',target_str)) #\b表示空格位置,或结束
结果匹配:er 
print(re.findall(r'\b\d',target_str))  #左边空格或开始边界,匹配第一个数字
结果匹配: 2

2,实战案例一

第一个目标:剔除字符串最开始的空格,或者保留一个空格

需求还是很简单的,最开始的空格,不管多少个,全部给他剔除或者保留一个。

正则中的 ^ 符号表示开始,用这个来检测处于最开始的位置的空格,代码如下:

target_str = '       1 2 3 4 5 67 89 0'
first = re.findall('^ +\S',target_str)
print(first)
two = re.findall('^( +)\S',target_str)
print(two)
target_str.replace(two[0],' ')
# ['       1']
# ['       ']
# ' 1 2 3 4 5 67 89 0'

目标字符串是' 1 2 3 4 5 67 89 0',除了开始有一串空格之外,中间也有空格。

示例代码中的两条规则,分别是'^ +S' '^( +)S',这两个的区别是什么?

翻阅一下之前的正则表格,括号代表组和所选内容的意思,所以:

  • 加上括号的意思,就是选择多个空格;
  • 不加括号,返回规则所匹配的内容;

看示例中的#位置,第一个是没有括号的,多了个1;第二个是加了括号的,没有1,因为1不是在括号内。括号选中的部分,是匹配到的所有空格。

最后用python字符串的replace操作,替换到空格即可,完成删除头部字符串的需求。

第二个目标:删除末尾的空格。

这个需求和第一个刚好相反,这次是去除末尾的多余空格,所以这次要用的,是正则表达式的结尾符号 $。

下面是示例代码:

# 剔除字符串最后的空格
target_str = '       a 4g 0n 2 0823 -k30 k03  b    c 3bbg9upb23pg 9n mgmg iopogo-n-   i- -442 -2 4-2        '
first = re.findall('\S +$',target_str)
print(first)
two = re.findall('\S( +)$',target_str)
print(two)
target_str.replace(two[0],' ')
# ['2        ']
# ['        ']
# '       a 4g 0n 2 0823 -k30 k03  b    c 3bbg9upb23pg 9n mgmg iopogo-n-   i- -442 -2 4-2 '

代码和前面几乎没差,就是 ^ 和 $ 的区别。

第三个目标:将两个或两个以上的空格,全部缩减成一个

这个需求和前面是不同的,但是也很简单,匹配思路就一个,提取两个或以上空格,统一替换成一个空格

如下代码:

# 将多个空格,删除并保留一个
target_str = '  a 4g 0n 2 0823 -k30   k03  b    c 3bbg9   upb23pg 9n mgmg iopogo-n-   i- -442 -2 4-2'
first = re.findall(' {2,}',target_str)
print(first)
for f in first:
    target_str = target_str.replace(f,' ')
print(target_str)
# ['  ', '   ', '  ', '    ', '   ', '   ']
# a 4g 0n 2 0823 -k30 k03 b c 3bbg9 upb23pg 9n mgmg iopogo-n- i- -442 -2 4-2

示例代码中所使用的规则,是' {2,}',直接是匹配2个或者以上的空格,非常直观。

拿到结果后,循环并做替换操作,可以保证所有的多个空格,都被替换成一个空格。

底部的#是输出结果,可以仔细对比看看

第四个目标:判断字符串是否符合限定条件

这个需求,在前端非常容易碰到,常见的场景是注册账号,账号、密码等字段的给定要求

例如只允许使用数字、小写字母、大写字母和下划线,以及长度在8-16中,下面是示例代码:

target_str_list = ['123','fh289f','s0fh23f fsfe','8324723j','*jsffjew)','23894husdfOP_JIJIMfe','*&^&*','deli2784090']
re_pattern = '[0-9a-zA-Z_]{8,16}'
for ts in target_str_list:
    result = re.findall(re_pattern, ts)
    if len(result)==1 and result[0]==ts:
        print(result)
# ['8324723j']
# ['deli2784090']

限定条件的匹配规则,也比较简单,数字、大小写字母、下划线都可以放到中括号中,字符串的长度可以放到大括号内,所以规则是'[0-9a-zA-Z_]{8,16}'

循环取字符串并做正则匹配,将结果做个if判断,这里的判断条件有点多:

  • 首先是result这个列表长度必须是1个,不能没有,也不能有多个,这都是不符合情况的。
  • 且result列表第一个内容必须和字符串相等,这才是百分百的匹配

特殊的疑问

看到这,你可能会有两点疑问:

  • 代码的鲁棒性,result[0]可能会出现数组越界的错误
  • 这段代码,主要是python判断,不是正则的匹配测试

先解答第一个问题,由于result[0]在if len(result)==1 and result[0]==ts:这个代码中,这里if的and判断条件,两个条件的顺序是从左到右,所以就有两种情况:

  • 第一种,len(result)==1判断不成立,if判断直接跳过,result[0]==ts不执行,不会报错
  • 第二种,len(result)==1判断成立,result[0]==ts执行,那此时result的长度必须是1,所以不会报错,正常执行

第一个问题的答案总结:由于有了len(result)==1这个前提,所以不会出现数组越界的情况。但是如果你把这两个条件换个位置,就会出现。

第二个问题,实现一个需求,如果可以最快最短的程序完成,那就最好;如果不能一步搞定,那就两步。解决需求的代码,必须简单易懂,尽量保证程序容易阅读,这样方便维护

另外补充一句,主流编程语言都支持正则表达式,但是使用方式都是不同的。python中是re库,js中是可以直接写正则的,都是“正则语法+编程语言语法”达到实现需求的目的。

3,实战案例二

案例一练习空格,实际场景少,本节课我们来练习邮箱,这个是非常常见的,不管前端后端。

先以QQ邮箱举例,每个qq号都可以申请一个qq邮箱,邮箱格式是数字@qq.com,另外qq邮箱还可以申请英文邮箱名,也就是qq号可以改成字母+数字的形式,所以这个正则还是最基础的,如下代码:

import re
target_mail_list = ['[email protected]','[email protected]','1121031509qq.com','[email protected]']
for tm in target_mail_list:
    result = re.findall("[a-z0-9A-Z][email protected]\.com",tm)
    print(result)
#['[email protected]']
#[]
#[]
#[]

目标字符串有四个字符串,除了第一个,其余三个都是漏了个别符号的。

QQ邮箱格式是数字@qq.com,所以规则可以直接写成"[a-z0-9A-Z][email protected]",结果只有第一个通过,其余的都不是。

补充说明

另外,补充一个操作,就是python的re库,findall是查找字符串中符合规则的字符并返回,我们这里做的操作是检测匹配,不是很符合,所以下面贴一个re库的匹配函数,如下代码:

target_mail_list = ['[email protected]','[email protected]','1121031509qq.com','[email protected]']
for tm in target_mail_list:
    result = re.match("[a-z0-9A-Z_][email protected]\.com",tm)
    if result:
        print(result, tm)
# <_sre.SRE_Match object; span=(0, 17), match='[email protected]'> [email protected]

re.match是匹配有的函数,检测字符串中,是否符合正则规则,这个函数在这个场景,会更适合。

多样化的邮箱地址

萝卜白菜各有所爱,很多人都不喜欢QQ邮箱,例如有说163的邮箱更专业些,还有就是一些国外的企业邮箱,例如yaho、gmail都是可以的。

所以邮箱的正则检测,还需要兼容一些其他的邮箱格式,所以在@符号后面,需要一些可以配置的内容,如下代码:

mail_type = '(qq|163|126|yaho|gmail)'
target_mail_list = ['[email protected]','[email protected]','1121031509qq.com','[email protected]',
                    '[email protected]','[email protected]','[email protected]','[email protected]','[email protected]']
for tm in target_mail_list:
    result = re.match(f"[a-z0-9A-Z][email protected]{mail_type}.com",tm)
    if result:
        print(result,tm)
#<_sre.SRE_Match object; span=(0, 17), match='[email protected]'> [email protected]
#<_sre.SRE_Match object; span=(0, 18), match='[email protected]'> [email protected]
#<_sre.SRE_Match object; span=(0, 13), match='[email protected]'> [email protected]
#<_sre.SRE_Match object; span=(0, 14), match='[email protected]'> [email protected]
#<_sre.SRE_Match object; span=(0, 17), match='[email protected]'> [email protected]

正则基础规则解释:

  • 小括号 () 代表组、范围;
  • | 代表或者;

所以mail_type参数中的内容,是专门放@符号后面的格式类型,采用python的f-string字符串拼接方式进行规则的拼接f"[a-z0-9A-Z][email protected]{mail_type}.com",得到正则规则

然后循环,取出待测试字符串,逐个测试,底部带#的,是输出结果。

4,实战案例三

手机号匹配

这节课,学习下手机号和身份证的正则规则编写,难度比前面也有提高。

首先是手机号,手机号是11位的,且第一位一定是1,随意规则中,第一个可以固定;

手机号的第二位,我目前见过的,只有3、5、6、7、8,没有1、2、4、9,所以第二位是可选35678,是小括号。

最后的9位,没有特定的数字,所以统一用d{9}占位。代码如下:

import re
phones = ['13748599409', '14499038847','13277678183', '19800987833', '22364758930',
          '133777488931','1783647228', '3367288919',  '45592345455', '19938842273']
for p in phones:
    result = re.match('^1[35678]\d{9}$',p)
    if result:
        print(result, p)
#<_sre.SRE_Match object; span=(0, 11), match='13748599409'> 13748599409
#<_sre.SRE_Match object; span=(0, 11), match='13277678183'> 13277678183

正则规则是'^1[35678]d{9}$',这里有开始符号和结尾符号。

一共有10个待测试手机号,大部分是不符合的,有少一位、多一位、第二位不符合的。

示例代码底部的#是输出结果,可以对比看看。

身份证匹配上

身份证号16位,这个比较难,因为身份证的构成是公开的,正则规则的编写比较苛刻,当然这里不会太难。

首先是前6位,这6位代表省【市】2位、市2位、区【县】2位,这里仅仅对省【市】做要求,如下的地区代码:

area = {"11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古", "21": "辽宁", "22": "吉林", "23": "黑龙江", "31": "上海",
          "32": "江苏", "33": "浙江", "34": "安徽", "35": "福建", "36": "江西", "37": "山东", "41": "河南", "42": "湖北", "43": "湖南",
          "44": "广东", "45": "广西", "46": "海南", "50": "重庆", "51": "四川", "52": "贵州", "53": "云南", "54": "西藏", "61": "陕西",
          "62": "甘肃", "63": "青海", "64": "宁夏", "65": "新疆", "71": "台湾", "81": "香港", "82": "澳门", "91": "国外"}

前6位的前两位是固定的,只能从这里选,所以前两位的正则规则,用代码计算出来,如下代码:

area = {"11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古", "21": "辽宁", "22": "吉林", "23": "黑龙江", "31": "上海",
          "32": "江苏", "33": "浙江", "34": "安徽", "35": "福建", "36": "江西", "37": "山东", "41": "河南", "42": "湖北", "43": "湖南",
          "44": "广东", "45": "广西", "46": "海南", "50": "重庆", "51": "四川", "52": "贵州", "53": "云南", "54": "西藏", "61": "陕西",
          "62": "甘肃", "63": "青海", "64": "宁夏", "65": "新疆", "71": "台湾", "81": "香港", "82": "澳门", "91": "国外"}
area_list = [a for a in area.keys()]
area_num = "|".join(area_list)
area_num = "("+ area_num +")"
print(area_num)
#(11|12|13|14|15|21|22|23|31|32|33|34|35|36|37|41|42|43|44|45|46|50|51|52|53|54|61|62|63|64|65|71|81|82|91)

前两位固定了,则前四位就是再拼接一下d{4},搞定6位,参数是area_complete。

身份证匹配中

接下来是年份4位,按现在的时间来看,1900~2099都是足够的,但还是最好拓展一下,1900~2199。

所以年份4位,分前两位和后两位,代码如下:

year_p = '(19|20|21)'
year_n = '\d{2}'

接下来是月份,01到09,还有10 11 12,这是不一样的,所以规则如下:

month = '((0[1-9])|10|11|12)'

那具体的月的某一天,可以是01~09、10~19、20~29、30、31,规则如下:

day = '((0[1-9])|(1[0-9])|(2[0-9])|30|31)'

可能会有人有以为,2月没有30,闰年2月最多29号。当然这里没有更多考虑,月份的具体天数不好绑定,那样规则太长了。宁可放宽限制,不要压的太死。

最后是四位表示位,最后一个可以是x,所以不能直接d{4}就完事,应该是按3+1的方式,如下代码:

bs_p = '\d{3}'
bs_n = '(\d|x)'

规则:三个数字+一个数字或者一个x

身份证匹配下

最后,将整个规则合并起来,得到16位的身份证规则,如下代码:

area_complete = area_num +"\d{4}"
year_p = '(19|20|21)'
year_n = '\d{2}'
month = '((0[1-9])|10|11|12)'
day = '((0[1-9])|(1[0-9])|(2[0-9])|30|31)'
bs_p = '\d{3}'
bs_n = '(\d|x)'
sfz_re = area_complete+year_p+year_n+month+day+bs_p+bs_n
print(sfz_re)
#(11|12|13|14|15|21|22|23|31|32|33|34|35|36|37|41|42|43|44|45|46|50|51|52|53|54|61|62|63|64|65|71|81|82|91)\d{4}(19|20)\d{2}((0[1-9])|10|11|12)((0[1-9])|(1[0-9])|(2[0-9])|30|31)\d{3}(\d|x)

略长

效果测试

这是我们学习中最长的一段规则,最后来测试一下,如下:

sfz_list = ['360000199108123664','110000200303188473','470000199108123664','11000020030318847x','340000200303298473']
for sfz in sfz_list:
    result = re.match(sfz_re, sfz)
    print(result,sfz)
#<_sre.SRE_Match object; span=(0, 18), match='360000199108123664'> 360000199108123664
#<_sre.SRE_Match object; span=(0, 18), match='110000200303188473'> 110000200303188473
#None 470000199108123664
#<_sre.SRE_Match object; span=(0, 18), match='11000020030318847x'> 11000020030318847x
#<_sre.SRE_Match object; span=(0, 18), match='340000200303298473'> 340000200303298473

总共5个假身份证号,只有中间的匹配失败,失败原因是47这省市不存在,其余的都通过。

5,常用正则表达式总结

数字

一、校验数字的表达式

  • 数字:^[0-9]*$
  • n位的数字:^d{n}$
  • 至少n位的数字:^d{n,}$
  • m-n位的数字:^d{m,n}$
  • 零和非零开头的数字:^(0|1-9*)$
  • 非零开头的最多带两位小数的数字:^(1-9*)+(.[0-9]{1,2})?$
  • 带1-2位小数的正数或负数:^(-)?d+(.d{1,2})?$
  • 正数、负数、和小数:^(-|+)?d+(.d+)?$
  • 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
  • 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
  • 非零的正整数:^[1-9]d$ 或 ^([1-9][0-9]*){1,3}$ 或 ^+?1-9$
  • 非零的负整数:^-[1-9][]0-9″*$ 或 ^-[1-9]\d*$
  • 非负整数:^d+$ 或 ^[1-9]\d*|0$
  • 非正整数:^-[1-9]d*|0$ 或 ^((-\d+)|(0+))$
  • 非负浮点数:^d+(.d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
  • 非正浮点数:`^((-d+(.d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d.\d|0.\d[1-9]\d))|0?.0+|0$
  • 正浮点数:^[1-9]d.d|0.d[1-9]d$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
  • 负浮点数:^-([1-9]d.d|0.d[1-9]d)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
  • 浮点数:^(-?d+)(.d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$

字符

二、校验字符的表达式

  • 汉字:^[u4e00-u9fa5]{0,}$
  • 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
  • 长度为3-20的所有字符:^.{3,20}$
  • 由26个英文字母组成的字符串:^[A-Za-z]+$
  • 由26个大写英文字母组成的字符串:^[A-Z]+$
  • 由26个小写英文字母组成的字符串:`^[a-z]+$
  • 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
  • 由数字、26个英文字母或者下划线组成的字符串:^w+$ 或 ^\w{3,20}$
  • 中文、英文、数字包括下划线:^[u4E00-u9FA5A-Za-z0-9_]+$
  • 中文、英文、数字但不包括下划线等符号:^[u4E00-u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
  • 可以输入含有^%&’,;=?$\”等字符:[^%&',;=?$x22]+
  • 禁止输入含有~的字符:3+

特殊表达式

三、特殊需求表达式

  • Email地址:^w+([-+.]w+)@w+([-.]w+).w+([-.]w+)*$
  • 域名:a-zA-Z0-9{0,62}(/.a-zA-Z0-9{0,62})+/.?
  • InternetURL:[a-zA-z]+://4 或 ^http://([w-]+.)+[w-]+(/[w-./?%&=])?$
  • 手机号码:^1[35678]d{9}$
  • 电话号码:^($$\d{3,4}-)|\d{3.4}-)?\d{7,8}$
  • 国内电话号码:d{3}-d{8}|d{4}-d{7}
  • 身份证号18位数字:^(11|12|13|14|15|21|22|23|31|32|33|34|35|36|37|41|42|43|44|45|46|50|51|52|53|54|61|62|63|64|65|71|81|82|91)d{4}(19|20)d{2}((0[1-9])|10|11|12)((0[1-9])|(1[0-9])|(2[0-9])|30|31)d{3}(d|x)$
  • 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
  • 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^a-zA-Z{4,15}$
  • 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]w{5,17}$
  • 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.d)(?=.[a-z])(?=.*[A-Z]).{8,10}$
  • 日期格式:^d{4}-d{1,2}-d{1,2}
  • 一年的12个月(01~09和10~12):^(0?[1-9]|1[0-2])$
  • 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
  • 钱的输入格式:
  • 有四种钱的表示形式我们可以接受:”10000.00″ 和 “10,000.00″, 和没有 “分” 的 “10000″ 和 “10,000″:^1-9$这表示任意一个不以0开头的数字,但是,这也意味着一个字符”0″不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?1-9)$这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$必须说明的是,小数点后面至少应该有1位数,所以”10.”是不通过的,但是 “10″ 和 “10.2″ 是通过的:^[0-9]+(.[0-9]{2})?$这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$这样就允许用户只写一位小数。下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
  • xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\.x|X[l|L]$
  • 中文字符的正则表达式:[u4e00-u9fa5]
  • 双字节字符:5 (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
  • 空白行的正则表达式:ns*r (可以用来删除空白行)
  • HTML标记的正则表达式:<(S?)6>.?</1>|<.? />
  • 首尾空白字符的正则表达式:^s|s$或(^\s*)|(\s*$)
  • 腾讯QQ号:1-9{4,} (腾讯QQ号从10000开始)
  • 中国邮政编码:[1-9]d{5}(?!d) (中国邮政编码为6位数字)
  • IP地址:d{1,3}.d{1,3}.d{1,3}.d{1,3} (提取IP地址时有用)
  • IP地址:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))

6,Python管理系统文件

电脑中如果文件很多且乱,需要花很多时间去整理的时候,你可以尝试使用Python去管理文件。

Python自带os库,用于操作系统的库,使用方便,本节课就来学习并整理文件。

首先是代码文件的所在目录和结构,截图看下:

首先导入os库,如下:

import os

导入之后,使用os.getcwd(),就可以获取当前文件所在的文件夹路径了,如下:

os.getcwd() #获取当前工作目录路径
# 'path_to\\源码文件夹\\8.使用Python管理文件'

为了看起来直观,省略了一部分的路径。

除了getcwd函数,还可以使用os的绝对路径函数,获取当前工作目录的路径,如下:

os.path.abspath('.') #获取当前工作目录路径
# 'path_to\\源码文件夹\\8.使用Python管理文件'

文件夹路径

现在来获取当前文件夹中的路径,并加上里面的tg_dir文件夹,保存到变量中,如下:

tgdir = os.path.abspath('tg_dir') #获取当前目录文件下的工作目录路径
print(tgdir)
# path_to\\源码文件夹\\8.使用Python管理文件\tg_dir

tg_dir文件夹是和ipynb代码放在一起的,这个文件夹里,放着一个名称sources的文件夹,使用os的函数,拼接出sources的路径,如下:

sources = os.path.join(tgdir,'sources')
print(sources)
# path_to\\源码文件夹\\8.使用Python管理文件\tg_dir\sources

读取文件夹内容

有了sources文件夹,就可以获取到文件夹中的文件以及文件夹,使用os.walk函数,如下:

for root, dirs, files in os.walk(sources):
    print(root)
    print(dirs)
    print(files)
#path_to\8.使用Python管理文件\tg_dir\sources
#[]
#['1517282865454.png', '1523348726316.png', '1531706079197_【1127】-【Django基础教程】.png', '1536283668551_【1152】-【SpringBoot入门教程】.png', '1539677989499_[1182]-[C++-使用-openGL-实现吃豆人游戏].jpg', '1539678114813_[1166]-[C++-实现-STL-库的组件和算法].jpg', '1542591740014_【32】-【Struts框架教程】.png', '1542592801417_【596】-【Python3简明教程】.png', '1546500900109.png', '1547620906601.png', '1548126810319.png', '1548126873144.png', '1548916517593.png', '1550728729236_【1035】-【Git与GitHub入门实践】(1).png', '1553137229452.png', '1557197583122_【1283】-【机器学习开放基础课程】.png', 'ncn109.jpg', 'ncn110.jpg']

os.walk函数,接收一个文件夹路径,返回三个参数,这里用root、dirs和files进行接收,解释下:

  • root:字符串格式,是当前文件夹的路径
  • dirs:列表格式,当前文件夹中,所包含的所有文件夹 dirs变量是可以自定义名称的
  • files:列表格式,当前文件夹中的所有文件名 files变量是可以自定义的,

7,拷贝文件并重命名

需求说明

掌握了基础的os管理文件函数,本节课来做一个小练习:

  • 读取tg_dir中的所有文件,复制到new文件夹中
  • 由于文件名不规范,提取文件名中的数字给文件重命名,文件保留原后缀格式

需求比较简单,拷贝并重命名,现在来用python实现它。

准备库和路径

首先,导入所需库,第一个是os,第二个是re,第三个是shutil,拷贝用的,如下代码:

import os
import shutil
import re

然后就是几个目录的路径了,tg_dir、sources以及new这三个文件夹的路径,如下:

tgdir = os.path.abspath('tg_dir')
print(tgdir)

sources = os.path.join(tgdir,'sources')
print(sources)

new = os.path.join(tgdir,'new')
print(new)
#path_to\9.拷贝文件并重命名\tg_dir
#path_to\9.拷贝文件并重命名\tg_dir\sources
#path_to\9.拷贝文件并重命名\tg_dir\new

获取文件夹中的文件名

准备就绪,第一步,先获取出sources中的所有文件,如下代码:

for root,dirs,files in os.walk(sources):
    for file in files:
        print(file)
#1517282865454.png
#1523348726316.png
#1531706079197_【1127】-【Django基础教程】.png
#1536283668551_【1152】-【SpringBoot入门教程】.png
#1539677989499_[1182]-[C++-使用-openGL-实现吃豆人游戏].jpg
#1539678114813_[1166]-[C++-实现-STL-库的组件和算法].jpg
#1542591740014_【32】-【Struts框架教程】.png
#1542592801417_【596】-【Python3简明教程】.png
#1546500900109.png
#1547620906601.png
#1548126810319.png
#1548126873144.png
#1548916517593.png
#1550728729236_【1035】-【Git与GitHub入门实践】(1).png
#1553137229452.png
#1557197583122_【1283】-【机器学习开放基础课程】.png
#ncn109.jpg
#ncn110.jpg

文件名比较难看,不规律.

正则匹配内容

现在使用正则,将文件名中的数字和后缀名都取出来,如下代码:

for root,dirs,files in os.walk(sources):
    for file in files:
        name = re.findall('\d+',file)
        type_name = re.findall('.(png|jpg)',file)
        print(name,type_name)
#['1517282865454'] ['png']
#['1523348726316'] ['png']
#['1531706079197', '1127'] ['png']
#['1536283668551', '1152'] ['png']
#['1539677989499', '1182'] ['jpg']
#['1539678114813', '1166'] ['jpg']
#['1542591740014', '32'] ['png']
#['1542592801417', '596', '3'] ['png']
#['1546500900109'] ['png']
#['1547620906601'] ['png']
#['1548126810319'] ['png']
#['1548126873144'] ['png']
#['1548916517593'] ['png']
#['1550728729236', '1035', '1'] ['png']
#['1553137229452'] ['png']
#['1557197583122', '1283'] ['png']
#['109'] ['jpg']
#['110'] ['jpg']

新文件名称拼接

然后就拼接,将新的文件名拼接出来,如下:

for root,dirs,files in os.walk(sources):
    for file in files:
        name = re.findall('\d+',file)
        type_name = re.findall('.(png|jpg)',file)
        filename = "{}.{}".format(name[0],type_name[0])
        print(filename)
#1517282865454.png
#1523348726316.png
#1531706079197.png
#1536283668551.png
#1539677989499.jpg
#1539678114813.jpg
#1542591740014.png
#1542592801417.png
#1546500900109.png
#1547620906601.png
#1548126810319.png
#1548126873144.png
#1548916517593.png
#1550728729236.png
#1553137229452.png
#1557197583122.png
#109.jpg
#110.jpg

拷贝文件

最后就是拷贝文件了,用shutil库的copyfile函数,示例代码shutil.copyfile(old_path,new_path),函数简单,将旧路径的文件,赋值给新路径就可以了

拷贝图片的完整代码,如下:

for root,dirs,files in os.walk(sources):
    for file in files:
        name = re.findall('\d+',file)
        type_name = re.findall('.(png|jpg)',file)
        filename = "{}.{}".format(name[0],type_name[0])
        old_name = os.path.join(sources,file)
        new_name = os.path.join(new,filename)
        shutil.copyfile(old_name, new_name)

  1. xyz
  2. a-z
  3. ~x22
  4. s
  5. x00-xff
  6. >
Last modification:April 27th, 2020 at 06:40 pm
如果觉得我的文章对你有用,请随意赞赏