第十六章:内置模块(re、hashlib、subprocess、logging)
正则表达式
官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个 “规则字符串”,这个 “规则字符串” 用来表达对字符串的一种过滤逻辑。
正则表达式是一门独立的技术 ,所有编程语言都可以使用。
它的作用可以简单的概括为:利用一些特殊符号(也可以直接写需要查找的具体字符)的组合产生一些特殊的含义然后去字符串中筛选出符合条件的数据,即:筛选数据(匹配数据)。
判断手机号是否合法
#re 模块实现
import re
phone_number = input('please input your phone number : ')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_number):
print('是合法的手机号码')
else:
print('不是合法的手机号码')
字符组
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用 [] 表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置 " 只能出现一个数字 ",那么这个位置上的字符只能是 0、1、2...9 这 10 个数之一。
字符组 | 描述 |
---|---|
[0123456789] | 匹配 0 到 9 任意一个数(全写) |
[0-9] | 匹配 0 到 9 任意一个数(缩写) |
[a-z] | 匹配 26 个小写英文字母 |
[A-Z] | 匹配 26 个大写英文字母 |
[0-9a-zA-Z] | 匹配数字或者小写字母或者大写字母 |
字符
元字符 | 匹配内容 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 (word) |
\s | 匹配任意的空白符 (space) |
\d | 匹配数字 (digit) |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符a或字符b |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
量词
量词 | 用法说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
重复 n 次 | |
重复 n 次或更多次 | |
重复 n 到 m 次 |
.*? 的用法
合在一起就是 取尽量少 的任意字符,一般不会这么单独写,大多用在:(.*?x)取前面任意长度的字符,直到一个 x 出现的时候
关键字符 | 用法说明 |
---|---|
. | 是任意字符 |
* | 是取 0 至 无限长度 |
? | 非贪婪模式 |
其他重要的字符
字符 | 用法说明 |
---|---|
\ | 转义符 |
{1,2}匹配 1 到 2 次任意字符 |
贪婪匹配
贪婪格式 | 用法说明 |
---|---|
<.*> | 默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | 加上 ?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
几个常用的非贪婪匹配(Pattern)
格式 | 用法 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
re 模块
三个非常重要的方法:findall、search、match
findall(找所有)
返回所有满足匹配条件的结果,放在列表里
import re
ret = re.findall('a','eva egon yuan')
print(ret) # ['a', 'a']
ret = re.findall('[a-z]+','eva egon yuan')
print(ret) # ['eva', 'egon', 'yuan']
获取式子中的整数
# 获取整数
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']
search(找一个)
从前往后,找到一个就返回,返回的变量需要调用 group 才能拿到结果
如果没有找到,那么返回 None,调用 group 会报错
# search
ret = re.search('a', 'eva egon yuan').group() #当不使用 group() 调用时,返回的是一个结果的对象
print(ret) # a
ret = re.search('a', 'eva egon yuan')
print(ret) # <re.Match object; span=(2, 3), match='a'>
# 没有找到值 会报错
# ret = re.search('j', 'eva egon yuan').group()
# print(ret) # AttributeError: 'NoneType' object has no attribute 'group'
# search 的一般用法
ret = re.search('a', 'eva egon yuan')
if ret:
print(ret.group()) # a
search 结合分组的用法
ret = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','110105199912122277')
print(ret.group()) # 110105199912122277
print(ret.group(1)) # 10105199912122 取出第一组
print(ret.group(2)) # 277 取出第二组
分组命名
# 可以在分组中利用?<name>的形式给分组起名字
# 获取的匹配结果可以直接用group('名字')拿到对应的值
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>hello</h1>")
print(ret.group()) # <h1>hello</h1>
print(ret.group(1)) # h1
print(ret.group('tag_name')) # h1
ret = re.search(r"<(\w+)>\w+</\1>", "<h1>hello</h1>")
# 如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# 获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1)) # h1
print(ret.group()) # <h1>hello</h1>
match(从头开始找一个)
用法 search 基本一致
match 是从头开始匹配,如果正则规则从头开始可以匹配上,就返回一个变量。
匹配的内容需要用 group 才能显示,如果没匹配上,就返回 None,调用 group 时会报错
# match
ret = re.match('a','eva egon,yuan') #没有匹配到
if ret:
print(ret.group())
ret = re.match('e','eva egon,yuan') #匹配到了
if ret:
print(ret.group()) # e
ret = re.match('[a-z]','eva egon,yuan') #匹配到了
if ret:
print(ret.group()) # e
hashlib 加密模块
# 加密算法基本操作
# 1.选择加密算法
md5 = hashlib.md5()
# 2.传入明文数据
md5.update(b'hello')
# 3.获取加密密文
res = md5.hexdigest()
print(res) # 5d41402abc4b2a76b9719d911017c592
# 密码加盐
def hash_salt_pwad(pawd):
obj = hashlib.md5(b'www.123.con')
obj.update(pawd.encode('utf-8'))
return obj.hexdigest()
ret = hash_salt_pwad('password_1231')
print(ret) # 080a3053fc4d9c69304e87b900784ba5
subprocess 模块
import subprocess
res = subprocess.Popen('ping www.baidu.com', shell=True,
stdin=subprocess.PIPE, # 输入命令
stdout=subprocess.PIPE, # 输出结果
stderr=subprocess.PIPE # 表示当命令不存在的时候,把结果吸入到 stderr 管道
)
print('正确结果', res.stdout.read().decode('gbk')) # 获取操作系统执行命令之后的正确结果
print('错误结果', res.stderr) # 获取操作系统执行命令之后的错误结果
logging 日志模块
日志级别
日志信息只显示了大于等于 WARNING 级别的日志,这说明默认的日志级别设置为 WARNING
import logging
logging.debug('这是⼀个debug级别的⽇志信息')
logging.info('这是⼀个info级别的⽇志信息')
logging.warning('这是⼀个warning级别的⽇志信息')
logging.error('这是⼀个error级别的⽇志信息')
logging.critical('这是⼀个critical级别的⽇志信息')
# WARNING:root:这是⼀个warning级别的⽇志信息
# ERROR:root:这是⼀个error级别的⽇志信息
# CRITICAL:root:这是⼀个critical级别的⽇志信息
logging 的使用模板
# 定义显示格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中 name 为 getlogger 指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
# 自定义文件路径
LOG_DIR = os.path.join(BASE_DIR, 'log')
if not os.path.isdir(LOG_DIR):
os.mkdir(LOG_DIR)
LOGFILE_PATH = os.path.join(LOG_DIR, 'ATM.log')
# log配置字典
LOGGING_DIC = {
# 默认 无需修改
'version': 1, # 日志版本
'disable_existing_loggers': False, # 指明是否禁止默认配置的记录器
# 需要配置的项
# 定义日志显示格式
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
# 过滤日志
'filters': {},
# 输出,处理方式
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
# 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'class': 'logging.handlers.TimedRotatingFileHandler', # 按自然天进行分割,保存到文件
'when': 'midnight',
'formatter': 'standard',
'filename': LOGFILE_PATH, # 日志文件
# 'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 15,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文 log 乱码了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的 logger 配置
'': {
'handlers': ['default'], # 这里把上面定义的两个 handler 都加上,即 log 数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
}, # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
},
}
def get_logger(msg):
logging.config.dictConfig(LOGGING_DIC) # 自动加载字典中的配置
logger1 = logging.getLogger(msg)
return logger1
logger = get_logger('管理员模块')
logger.error(f'管理员{admin_name}冻结了{lock_username}账户')