博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

python day6 装饰器补充,正则表达式

Posted on 2019-10-09 14:44  bluestarpin  阅读(297)  评论(0编辑  收藏  举报

python day 6

2019/10/09

学习资料来自老男孩教育

1. 装饰器decorator

一旦挂上装饰器,函数就会变成装饰器的inner函数。
1.1. 接收任意个参数的装饰器。

def outter(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        return ret
    return inner

@outter
def hello(name):
    print('hello %s' % name)
hello('lanxing')

1.2. 多个装饰器装饰同一个参数

def outter1(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        return ret
    return inner

def outter2(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        return ret
    return inner

@outter1
@outter2
def hello(name):
    print('hello %s' % name)

hello('lanxing')

2. 正则表达式re

2.1 正则表达式概述

在开发中会有大量的字符串处理工作,其中经常会涉及到字符串格式的校验
例如:如何判断一个字符串是不是手机号(检测用户填写)
17611105750
11位;数字组成不能包含其他字符;1开头;第二位是3,4,5,7,8
如何将以上规则描述出来,让计算机可以识别,就是正则表达式要完成的工作。
正则表达式是一个特殊的字符序列:用来描述某个规则
正则表达式的大致匹配过程是:依次拿出表达式(规则)和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。
它能帮助你方便的检查一个字符串是否与某种模式匹配,也叫正规表示法、规则表达式、常规表示法。。。等等。常简写为RE。
英语:Regular Expression 这里的regular指‘规则’
Regular Expression 即 “描述某种规则的表达式”

正则表达式并不是Python的一部分
正则表达式是用于处理字符串的工具,拥有自己独特的语法以及一个独立的处理引擎,在提供了正则表达式的语言里,正则表达式的语法都是一样的。
(不同的编程语言支持的语法数量不同)

Python中需要通过正则表达式对字符串进⾏匹配的时候, 使⽤re模块。
re 模块是Python当中提供的正则表达式工具包,使Python语言拥有全部的正则表达式功能

2.2 re模块常用方法

  • match(匹配)

使用match方法进行匹配操作:
re.match 尝试从字符串的起始位置匹配一个模式

re.match(正则表达式,要匹配的字符串):

  1. match方法的第一个参数接收的是描述某个规则的字符串.
  2. 第二个参数是要进行校验的值.
  3. 从左向右开始匹配.

re.match()是用来进行匹配检查的方法,若正则表达式与字符串规则匹配,则返回匹配对象(Match Object),否则返回None.
匹配对象Match Object具有一个group方法,用来返回字符串中匹配的部分.
(如果匹配到数据的话, 可以使⽤group⽅法来提取匹配到的数据).

import re
line = 'qwertyuiop'    #不具备通用性
s = "qwertyuiopl"
print(re.match(line, s).group())


import re
line = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('Hello\s\d\d\d\s\d{4}\s\w{10}', line)
print(result)
print(result.group())
  • 表示字符
字符 功能 备注
. 点号表示匹配任意一个字符
[] 匹配中括号[]里面的一个字符
\d 表示0-9之间的数字,包括0与9 相当于[0-9]
\D 表示除了0-9的字符,即\d的取反 相当于[^0-9]
\w 匹配单词字符,即0-9,a-z,A-Z以及下划线_ 相当于[0-9a-zA-Z_]
\W 匹配非单词字符 相当于[^0-9a-zA-Z_]
\s 匹配空白符,即空格键,tab键\t,换行\n
\S 匹配非空白符

正则表达式⾥使⽤“\”作为转义字符(\d)。
由于正则表达式使用反斜杠来转义特殊字符,而python自身处理字符串时,反斜杠也是用于转义字符,这样就产生了一个双重转换的问题
假如你需要匹配⽂本中的字符"", 那么使⽤正则表达式⾥将需要4个反斜杠"\": 前两个和后两个分别⽤于在编程语⾔⾥转义成反斜杠, 转换成两个反斜杠后再在正则表达式⾥转义成⼀个反斜杠。
Python中字符串前⾯加上 r 表示原⽣字符串(真实字符串)
原⽣字符串很好地解决了上面的这个问题, 有了原始字符串, 你再也不⽤担⼼是不是漏写了反斜杠。
'r'是防止字符转义的 如果路径中出现'\t'的话 不加r的话\t就会被转义 而加了'r'之后'\t'就能保留原有的样子。
所以以后凡是使用正则表达式,就先加上r。
ret = re.match(r"c:\\a", "c:\\a\\b\\c" )
ret.group()
从字符串输入到代码转义一次,又在生成正则表达式的时候再转义一次,于是就需要多打反斜杠。用r""可以抵消掉第一次转义.
r只抵消第一次转义:python正则表达式加了r之后\w,\s,.这些有含义的字符仍然有效.

  • 表示数量
字符 功能
* 匹配前一个字符0次或多次,即可有可无,\d*表示匹配0个或多个数字
+ 匹配前一个字符1次或多次,即至少一次
? 匹配前一个字符1次或0次,即要么有1次,或者0次
匹配前一个字符m次
匹配前一个字符至少m次或无限次
匹配前一个字符从m次到n次,可以替代*+?
  • 表示边界
字符 功能
^ 匹配字符串开头,(必须以某个条件开头)
$ 匹配字符串结尾
\b 匹配单词边界,需要加r
\B 匹配非单词边界
print(re.match('^The', 'The end').group())
匹配以The开头的字符串

print(re.match('\w*\send$', 'The end').group())
匹配必须以end结尾
print(re.match(‘\w*\send$’, ‘The endd’).group()) #出错

`re.match(r".*\bver\b", "ho ver abc").group()`
`re.match(r".*\bver\b", "ho verabc").group()` 
`re.match(r".*\bver\b", "hover abc").group()`
\b匹配的位置:前面和后面的字符不全是\w匹配成功,反过来理解,如果\b的前面和后面都是\w,则不匹配

匹配大陆手机号
re.match("^1[34578]\d{9}$", "17611105750")
  • 匹配分组
字符 功能
| 匹配左右任意一个表达式(左或右)
(ab) 将括号中字符作为一个分组
\num 引用分组num匹配到的字符串
(?P<name>) 分组起别名
(?P=name) 引用别名为name分组匹配到的字符串
# 匹配1-100之间的数字:
ret = re.match(‘[1-9]\d?$|0$|100$',“8")
print(ret.group())

ret = re.match('[1-9]?\d$|100$','8')

分组就是用一对圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。
从正则表达式的左边开始看,看到的第一个左括号“(”表示第一个分组,第二个表示第二个分组,依次类推,需要注意的是,有一个隐含的全局分组(0),就是整个正则表达式。
分完组以后,要想获得某个分组的内容,直接使用group(num)和groups()函数提取

ret = re.match("\w{4,20}@163\.com", "test@163.com")
ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@126.com")
ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com")
ret = re.match("([^-]*)-(\d+)","010-12345678")
ret.group()
#'010-12345678'
ret.group(1)
#'010'
ret.group(2)
#'12345678'
ret.groups()

ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.sxt.cn</h1></html>")
ret.group()

ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.sxt.cn</h2></html>")
ret.group()

ret = re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.sxt.cn</h1></html>")
ret.group()

ret = re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.sxt.cn</h2></html>")
ret.group()
# 注意: (?P<name>) 和 (?P=name) 中的字⺟p⼤写
  • re模块的其他函数
  1. match: re.match(pattern,string,flags=0),从头开始匹配,匹配成功返回一个匹配对象,该匹配对象有group,start,end,span,groups等方法,用于返回匹配内容,匹配不成功则返回None,不成功时,没有group方法。flags是编译标志位,用于修改正则表达式的匹配方式,如:是否匹配大小写,是否多行匹配等。
    flags的实参有以下几种:
    1.1. re.I 使匹配对大小写不敏感。
    1.2. re.L 做本地化识别(locale-aware)匹配.
    1.3. re.M 多行匹配,影响^和$.
    1.4. re.S 使.匹配包括换行在内的所有字符
    1.5. re.U 根据Unicode字符集解析字符,影响\w,\W,\b,\B
    1.6. re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更灵活。
    匹配成功返回的对象的方法有以下几种:
    1.7. group(n=0) 不传参数,默认是0,返回re整体匹配的字符串,可以一次输入多个组号,对应组的组号。
    1.8. group(n,m) 返回组号为n,m所匹配的字符串,如果组号不存在,会报错。
    1.9. groups() 返回一个元组,元组中元素是正则表达式中每个小组匹配到的字符串。通常groups不加参数。
    1.10. start() 返回匹配开始的位置。
    1.11. end() 返回匹配结束的位置。
    1.12. span() 返回一个元组包含匹配(开始,结束)的位置。
    re.match('com','comww.runcomoob').group()
    re.match('com','Comww.runcomoob',re.I).group()
  2. search re.search(pattern,string,flags=0)搜索符合特征的第一个字符串,匹配成功返回一个匹配对象,该匹配对象有group,start,end,span,groups等方法,用于返回匹配内容,匹配不成功则返回None,不成功时,没有group方法:
    ret = re.search(r"\d+", "阅读次数为 9999")
    ret.group()
  3. findall 找出所有符合特征的字符串,并返回一个列表,不需要用group方法即可返回列表,finall优先返回组里面的内容,如果要取消,则在组前面加上?:;如果pattern有超过1个分组,则列表的元素是一个包含所有分组匹配内容的元组:
    In [32]: re.findall(r'(\d{0,4})','xyz024abc123')
    Out[32]: ['', '', '', '024', '', '', '', '123', '']
    In [33]: re.findall(r'(\d{0,4})(\w{0,3})(\d)','xyz024abc123')
    Out[33]: [('', 'xyz', '0'), ('24', 'abc', '1'), ('2', '', '3')]
  4. sub re.sub(pattern,repl,string,max=0)将匹配到的数据进⾏替换,返回替换后的字符串,max指最大替换多少次,默认0是全部替换。:
    ret = re.sub(r"\d+", '998', "python = 997")
    ret = re.sub(r'g.t','have','I get apple, I got orange, I gut banana.',2)
  5. split:re.split(pattern,string),根据匹配进⾏切割字符串,并返回⼀个列表:
    ret = re.split(r":| ","info:xiaoZhang 33 shandong")
    p = re.compile('\d')
    In [38]: p.split('one1two2three3four4')
    Out[38]: ['one', 'two', 'three', 'four', '']
  6. compile: re.compile(strPattern[,flag]) 如果要多次调用规则,则使用compile方法,先将规则编译好。
    regex = re.compile(r'\w*oo\w*'),regex.findall('helloo, good')
  7. finditer(pattern,string),返回的是包含匹配对象的迭代器。如果要获得匹配内容,得遍历迭代器得到匹配对象,再调用匹配对象的group方法。
    re.finditer(r'\d+','12drum44ers druming,11,drun12,13,14')
  • 贪婪和非贪婪

Python⾥正则表达式数量词( "*","?","+","{m,n}" )默认是贪婪的, 总是尝试匹配尽可能多的字符

s = "number is 123456789"
ret = re.match(r"(.+)(\d)",s)
print(ret.group(1))

⾮贪婪则相反, 总是尝试匹配尽可能少的字符:
在"*","?","+","{m,n}"后⾯加上? , 使贪婪变成⾮贪婪.
result = re.match(r"aa(\d+)","aa2343ddd").group(1)
'2343'
result = re.match(r"aa(\d+?)","aa2343ddd").group(1)
'2'

贪婪和非贪婪的到的结果也有可能是一样的
result = re.match(r"aa(\d+)ddd","aa2343ddd").group(1)
'2343'
result = re.match(r"aa(\d+?)ddd","aa2343ddd").group(1)
'2343'

作业:

import re
# 1. 判断字符串是否是全部小写
s1 = 'qwerty123A'
result1 = re.match('^[a-z]*$',s1)
if result1:
    print(result1.group())
else:
    print('不是全部小写,无法匹配')

# 2. 匹配由数字/26个英文字母组成的字符串
p2= '[0-9a-zA-Z]*'
s2 ='1323abcABC_xyz'
result2 = re.match(p2,s2)
print(result2.group())

# 3. 匹配长度为8-10的用户密码(以字母开头/数字/下划线)
p3 = '^[a-zA-Z]\w{7,9}$'
s3 = 'abc1323_xy'
result3 = re.match(p3,s3)
print(result3.group())

# 4. 电子邮箱验证
p4 = r'\w{4,20}@(163|126|qq)\.com'
s4 = '3768094lan@163.com'
result4 = re.match(p4,s4)
if result4:
    print(result4.group())

# 5. 简单的身份证号验证
p5 = r'[1-6]\d{5}(18|19|20)\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d$|X$)'
s5 = '36078219880631032X'

result5 = re.match(p5,s5)
if result5:
    print(result5.group())
else:
    print('不是身份证号码')


# 6. 提取并捕获html标签内容
p6 = r'<(?P<html>\w*)><(?P<h1>\w*)>(.*)</(?P=h1)></(?P=html)>'
s6 = '<html><h1>我爱你</h1></html>'
result6 = re.match(p6,s6)
if result6:
    print(result6.group(3))
else:
    print('不正确')