昨日内容补充:
对于两个文件中的互相导入md模块问题分析:
x = 1from md import xprint(x)# md中的xfrom md import xx = 1000print(x)# 当前文件中的x
__all__ 列表写什么,执行导入语句那个文件就能拿到什么;
__all__不写的情况,默认是将所在文件所有的名字都暴露给导入者.
解决循环导入的第三种方式(前两种已在day14涉及):
- 将循环导入的名字,放到另外一个文件中
正则表达式
定义: 正则就是用来筛选字符串中的特定的内容的,对字符串进行操作的逻辑工具
re模块与正则表达式之间的关系:
- 正则表达式不是python独有的,它是一门独立技术
- 所有的编程语言都可以用正则
- 如果想在python中用,就必须依赖于re模块
相关书籍: <正则指引>
验证工具: www.tool.chinaz.com
正则的应用场景:
- 1.爬虫
- 2.数据分析
注意点: 只要是reg...一般情况下都是跟正则有关,而且如果想匹配具体的内容,可以直接写完整的内容,不需要写正则
字符组[ ]:
正则 | 待匹配字符 | 匹配结果 | 说明 |
[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,用来验证十六进制字符 |
关于字符:
元字符 | 匹配内容 |
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符a或字符b |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
关于量词:
量词 | 用法说明 |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
注意点:
- 通常一个字符组里面的表达式都是或关系
- \w:\word与\W相反
- \s:\space与\S相反
- \d:\digit与\D相反
- ^匹配字符串的开始 类似于startswith
- $匹配字符串的结尾 类似于endswith
- ^如果直接写在外面,就是限制字符串开头的作用
- [^...]就是取反的意思,除了...之外其他都能输出
- a|b,如果是abc|ab,一定要将长的放在前面
PS: ^与$连用,就能精准限制匹配的内容,两者之间写什么,匹配的字符串就必须是什么,多一个不行,少一个也不行
例如:^jason$,最终输出结果就一定是jason
贪婪匹配Patten与非贪婪匹配:
正则 | 待匹配字符 | 匹配结果 | 说明 |
<.*> | <script>...<script> | <script>...<script> | 默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | r'\d' | <script> <script> | 加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
-
<.*>:先拿着里面的.*去匹配所有的内容,然后再根据>往回退着找,遇到即停止
-
<.*?>:先拿着?后面的>去匹配符合条件的最少的内容,然后把匹配的结果返回
PS:根据匹配的内部原理可以很好的理解, .*?x 就是取前面任意长度的字符,直到一个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 | 现在不会匹配错误的身份证号了 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 110105199812067023 | 110105199812067023 | 表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14} |
定义:当多个正则符号需要重复多次时,或者当作一个整体进行其他操作,那么可以分组
分组在正则的语法中就是( )
例如: 用[a-z][0-9]会匹配到三个结果,这个时候就可以用分组([a-z][0-9])+,可以提高不少效率
例题: 关于身份证的正则表达式
- ^[1-9]\d{14}(\d{2}[0-9x])?$
- ^([1-9]\d{16}[0-9x]|[1-9]\d{14})$
上面三种都是解决方法,第三种考虑的最全面
转义符:
正则 | 待匹配字符 | 匹配结果 | 说明 |
\n | \n | False | 因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配 |
\\n | \n | True | 转义\之后变成\\,即可匹配 |
'\\\\n' | '\\n' | True | 如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次 |
r'\\n' | r'\n' | True | 在字符串之前加r,让整个字符串不转义 |
注意点:
- 在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对""进行转义,变成'\'
- python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的
- 字符串中\也有特殊的含义,本身还需要转义
- 如果匹配一次"\n",字符串中要写成'\\n'
- 如果匹配一次"\\n",字符串中要写成'\\\\n'
- r'\\n' 在字符串前面加个r,让整个字符串不转义(了解:r其实就是real的意思,真实不转义)
re模块:
定义:python中使用正则必须借助于re模块,或者是支持正则表达式书写的功能与方法
while True:phone_number = 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('16') \or phone_number.startswith('17') \or phone_number.startswith('18')):print('是合法的手机号码')else:print('不是合法的手机号码')import rephone_number = input('please input your phone number : ')if re.match('^(13|14|15|16|17|18)[0-9]{9}$',phone_number):print('是合法的手机号码')else:print('不是合法的手机号码')
三种主要方法:
- findall: 找出字符串中符合正则表达式的全部内容,并且返回的是一个列表,列表中的元素就是正则匹配到的结果
- searfch: 只会依据正则查一次 只要查到了结果,就不会再往后查找;当查找的结果不存在的情况下 调用group直接报错
- match: 只会匹配字符串的开头部分;当字符串的开头不符合匹配规则的情况下 返回的也是None 调用group也会报错
ret = re.findall('[a-z]+', 'eva egon jason')# findall('正则表达式', '带匹配的字符串')print(ret) # ['eva', 'egon', 'jason']
ret = re.search('a', 'eva egon jason')# search('正则表达式', '带匹配的字符串')print(ret) # 返回 <_sre.SRE_Match object; span=(2, 3), match='a'> # search不会给你直接返回匹配到的结果,而是给你返回一个对象print(ret.group()) # a# 必须调用group才能看到匹配到的结果
ret1 = re.search('k', 'eva egon jason')print(ret1.group()) # 直接报错,因为print(ret)返回None,而且None没有内置group方法
ret = re.match('e', 'eva egon jason')print(ret) # 返回<_sre.SRE_Match object; span=(0, 1), match='e'> print(ret.group()) # e
ret = re.match('a', 'eva egon jason')print(ret) # 返回Noneprint(ret.group()) # 直接报错
其他方法:
split:分割
ret = re.split('[ab], 'abcd') # 先按'a'分割得到' '和'bcd',再对' '和'bcd'分别按'b'分割print(ret) # 返回的还是列表[' ', ' ', 'cd']
sub:替换,先按照正则表达式查找所有符合该表达式的内容 统一替换成'新的内容' 还可以通过n来控制替换的个数
ret = re.sub('\d', 'H','eva3egon4yuan4',1) # 将数字替换成'H',参数1表示只替换1个# sub('正则表达式', '新的内容', '待替换的字符串',n)print(ret) # 返回值为evaHegon4yuan4
subn:替换(返回元祖)
ret = re.subn('\d', 'H', 'eva3egon4yuan4') # 将数字替换成'H',返回元组(替换的结果,替换了多少次) ret1 = re.subn('\d', 'H', 'eva3egon4yuan4',1) # 将数字替换成'H',返回元组(替换的结果,替换了多少次)print(ret) # 返回的是一个元组 元组的第二个元素代表的是替换的个数
obj:编译(转换功能)
obj = re.compile('\d{3}') # 将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字 ret = obj.search('abc123eeee') # 正则表达式对象调用search,参数为待匹配的字符串 ret1 = obj.findall('347982734729349827384')print(ret.group()) # 返回结果是 123print(ret1) # 返回结果是 ['347', '982', '734', '729', '349', '827', '384']
finditer:返回一个存放匹配结果的迭代器
ret = re.finditer('\d', 'ds3sy4784a') # finditer返回一个存放匹配结果的迭代器print(ret) # 返回<callable_iterator object at 内存地址>print(next(ret).group()) # 等价于ret.__next__(),超出迭代取值的范围 直接报错StopIterationprint([i.group() for i in ret]) # 查看剩余的左右结果
扩展知识点:
1.?P可以给某一个正则表达式起别名
import reres = re.search('^[1-9] (\d{14})(\d{2}[0-9x])?$','110105199812067023')res = re.search('^[1-9] (?P<password>\d{14})(?P<username>\d{2}[0-9x])?$','110105199812067023')print(res.group()) # 返回值为110105199812067023print(res.group('password')) # 返回值为110105199812067print(res.group(1)) # 返回值为110105199812067print(res.group('username')) # 返回值为023print(res.group(2)) # 返回值为023
2.?:取消权限,findall就不会优先把匹配结果组里内容返回
ret1 = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')ret2 = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') # 忽略分组优先的机制print(ret1,ret2) # ['oldboy']
3.关于split的知识点
ret=re.split("\d+","eva3egon4yuan")print(ret) # 结果 : ['eva', 'egon', 'yuan'] ret1=re.split("(\d+)","eva3egon4yuan")print(ret1) # 结果 : ['eva', '3', 'egon', '4', 'yuan']