DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • python面向过程

    • Python语言介绍
    • 基本运算符和流程控制
    • 数据类型之序列
    • 数据类型之哈希
    • 函数基础
    • 函数高级
    • 函数进阶
    • 文件处理
    • 必用模块
      • re模块
        • 表达式/规则
        • 单个字符
        • 瞄点/边界
        • 数量表示
        • 贪婪与非贪婪!
        • 分组与引用
        • 预查断言
        • 常用函数
        • re.search()
        • re.match()
        • re.findall()
        • re.split()
        • re.sub()
        • re.compile()
      • logging模块
        • 第一种方式
        • 第二种方式
        • 纯享版
      • hashlib模块
        • hash三大特性
        • 文件一致性校验
        • 原理
        • 实现
        • 明文密码加密
        • 原理
        • 实现
        • 补充: hmac模块
      • subprocess模块
      • sys模块
      • json&pickle模块
        • 异同
        • json格式
        • 序列化
        • json模块的使用
        • json序列化
        • json反序列化
        • pickle模块的使用
    • 常用模块
    • 模块导入与包
    • 开胃小菜
    • 饶头训练
    • 小项目之ATM_shopping
  • python面向对象

  • 网络并发编程

  • 不基础的py基础

  • 设计模式

  • python_Need
  • python面向过程
DC
2023-09-05
目录

必用模块

re
logging
hashlib   hmac
subprocess
sys
json   pickle
1
2
3
4
5
6

# re模块

正则就是用一系列具有特殊含义的字符组成规则,该规则用来描述具有某一特征的字符串

常用正则,参考网址: https://r2coding.com/#/README?id=正则表达式 !!

# -- 'a\n'前面加r等同于'a\\c',告知解释器里面的\不是转义字符
>>> len('a\\n')
3
>>> len(r'a\n')
3
1
2
3
4
5

# 表达式/规则

# 单个字符

下方的正则表达式代表 一个 字符/原子.

表达式 描述
[abc] 原子表里面包含一组地位平等的原子, 取其中任意一个进行匹配
[^abc] 除了中括号里面的原子均可以匹配.
[a-z] 字符范围. 匹配指定范围内的任意字符
. 匹配除换行符以外的任何单个字符
\ 用于转义.
\w 匹配一个字母数字或下划线, 等价于[A-Za-z0-9_]
\W 匹配一个非字母数字下划线的字符, 等价于[^A-Za-z0-9_]
\d 匹配一个数字, 等价于 [0-9]
\D 匹配一个非数字字符
\s 匹配一个空白字符, 包括空格、制表符等.. 等价于 [\t\n\r\f]
\S 匹配一个非空白字符
\t 匹配一个制表符
\n 匹配一个换行符
>>> import re
# -- findall(pattern, string)  返回string中所有与pattern匹配的全部字符串,返回形式为数组
>>> re.findall('\s','hell\no 12\t3_ */-=')
['\n', ' ', '\t', ' ']
>>> re.findall('dc','dc is dc')
['dc', 'dc']
>>> re.findall('[ay3]','123apy')
['3', 'a', 'y']
>>> re.findall('[^y3]py','123apy')
['apy']
# \- 转义'使得 - 不再代表范围'
>>> re.findall('a[a-zA-Z+\-*/]c','abc a2c aVc a+c a-c a/c aaa\nc') 
['abc', 'aVc', 'a+c', 'a-c', 'a/c']
>>> re.findall('a[a-z][A-Z]c','abc abVc aaa1ac')
['abVc']
>>> re.findall('a.c','abc a2c aer aaa\nc aaa1ac')
['abc', 'a2c']
>>> re.findall('a.c','abc a2c aer aaa\nc aaa1ac',re.DOTALL) # -- re.DOTALL 使 . 包含换行符\n
['abc', 'a2c', 'a\nc']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 瞄点/边界
表达式 描述
^ 匹配字符串或行开头
$ 匹配字符串或行结尾
\b 匹配单词边界。比如Sheep\b可以匹配CodeSheep末尾的Sheep,不能匹配CodeSheepCode中的Sheep
\B 匹配非单词边界。比如Code\B可以匹配HelloCodeSheep中的Code,不能匹配HelloCode中的Code。
>>> re.findall('^dc','my name is dc')
[]
>>> re.findall('^dc','dcdc is cool!')
['dc']
1
2
3
4
# 数量表示

注意哦! *、?、+、{n,m} 不能单独使用, 搭配字符使用.

表达式 描述
? 代表?左侧的那个字符可出现0次或1次 {0,1}
+ 代表?左侧的那个字符可出现1次或无穷多次 {1,}
* 代表*左侧的那个字符可出现0次或无穷多次 {0,}
| 或运算符. 并集, 可以匹配符号前后的表达式 eg: a|b 匹配a或b
{m} 代表左侧的那个字符恰好出现m次
{m,} 代表左侧的那个字符至少出现m次
{m,n} 代表左侧的那个字符至少出现m次,至多出现n次

Ps: 表格中说左侧的字符, 不准确, 要注意正则表达式中存在分组的情况.

>>> re.findall('ac*12','a012 a12 acc12 acd12 accc12')
['a12', 'acc12', 'accc12']
>>> re.findall('ac?','a012 a12 acc12')
['a', 'a', 'ac']
>>> re.findall('ac?d','ad012 ac acd accd12 acc12')
['ad', 'acd']
>>> re.findall('cd{1,}','abcdddcdcdcd')
['cddd', 'cd', 'cd', 'cd']
1
2
3
4
5
6
7
8
# 贪婪与非贪婪!

.* : 贪婪匹配 (不够精准)
.*? : 非贪婪匹配

# -- .*  除换行符的任意字符可以出现0次获无穷次 换言之可以匹配除换行符外任意多个任意字符
# -- .*? 可以没有字符 ( ̄▽ ̄)
>>> re.findall('a.*','abs2131c12312kkjkasc23')
['abs2131c12312kkjkasc23']
>>> re.findall('a.*','abs2131c1\n2312kkjkasc23') # -- 注意哦,有个\n
['abs2131c1', 'asc23']
>>> re.findall('a.*','abs2131c1\n2312kkjkasc23',re.DOTALL)
['abs2131c1\n2312kkjkasc23']
>>> re.findall('a.*?','abs2131c12312kkjkasc23')
['a', 'a']

>>> re.findall('a.*c','abs2131c12312kkjkasc23')   # -- 贪婪,碰到c后还不停止
['abs2131c12312kkjkasc']
>>> re.findall('a.*?c','abs2131c12312kkjkasc23')  # -- 非贪婪,碰到c就停止
['abs2131c', 'asc']

>>> re.findall('a.*c?','abs2131c12312kkjkasc23')  # -- c可以出现0或1次,那不就等同于 'a.*' 嘛
['abs2131c12312kkjkasc23']
>>> re.findall('a.*?c?','abs2131c12312kkjkasc23') # -- 等同于 'a.*?'
['a', 'a']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 分组与引用
表达式 描述
(expression) 分组. 小括号括起来的字符会被当成一个整体来匹配
(?:expression) 非捕获分组.
匹配括号里的整个字符串但不获取匹配结果, 拿不到分组引用.
\num 对前面所匹配分组的引用. 比如 (\d)\1可以匹配两个相同的数字,(Code)(Sheep)\1\2则可以匹配 CodeSheepCodeSheep。
>>> re.findall('c(d){1,}','abcdddcdcdcd')
['d', 'd', 'd', 'd']
# -- 先得到['cdr','cdcdcdr'],然后再从结果中取'cd'
>>> re.findall('(cd){1,}r','abcdrddcdcdcdr')
['cd', 'cd']
>>> re.findall('(?:cd){1,}r','abcdrddcdcdcdr')
['cdr', 'cdcdcdr']

>>> re.findall('compan(?:ies|y)','too many companies hava ..., next company is ..')
['companies', 'company']
>>> re.findall('compan(ies|y)','too many companies hava ..., next company is ..')
['ies', 'y']

>>> re.findall('exp=".*"','exp="1+2+3/4*5" egon="cool"')
['exp="1+2+3/4*5" egon="cool"']
>>> re.findall('exp=".*?"','exp="1+2+3/4*5" egon="cool"')
['exp="1+2+3/4*5"']
>>> re.findall('exp="(.*?)"','exp="1+2+3/4*5" egon="cool"')
['1+2+3/4*5']
>>> re.findall('(exp)="(.*?)"','exp="1+2+3/4*5" egon="cool"')
[('exp', '1+2+3/4*5')]
# -- 'href="(.*?)"' 取网址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 预查断言
表达式 描述
(?=) 正向预查.
比如 Code(?=Sheep) 能匹配 CodeSheep 中的 Code , 但不能匹配 CodePig 中的 Code
(?!) 正向否定预查.
比如 Code(?!Sheep) 不能匹配 CodeSheep 中的 Code, 但能匹配 CodePig中的Code
(?<=) 反向预查.
比如(?<=Code)Sheep能匹配 CodeSheep中的Sheep,但不能匹配ReadSheep中的Sheep
(?<!) 反向否定预查
比如(?<!Code)Sheep不能匹配 CodeSheep中的Sheep,但能匹配ReadSheep中的Sheep

# 常用函数

# re.search()

re.search(): 从左往右匹配,匹配成功一个就结束

若存在返回对象,不存在返回None
注意,search方法中的正则加了括号进行分组,取的依旧是整体

>>> re.search('dc','my name is dc, dc is cool.')  # -- 只会找到一个,返回的是对象
<re.Match object; span=(11, 13), match='dc'>
>>> re.search('dc','my name is dc, dc is cool.').group()  # -- 将对象的结果拿出来
'dc'
>>> re.search('d(c)','my name is dc, dc is cool.')  # -- search加了括号取的也是整体
<re.Match object; span=(11, 13), match='dc'>
>>> re.search('abcd','my').group()  # -- 没有找到,返回None. None对象是没有group()方法的,报错.
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    re.search('abcd','my').group()
AttributeError: 'NoneType' object has no attribute 'group'
  
# -- 相当于,在search方法中 'd(dc){2}'等同于'd(:?dc){2}'
>>> re.search('d(dc){2}','my name is ddc, ddcdc is cool.').group()
'ddcdc'
>>> re.search('d(:?dc){2}','my name is ddc, ddcdc is cool.').group()
'ddcdc'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# re.match()

有点类似于search, 不同于的是它是从头开始匹配,开头不符合就返回None.

>>> re.search('my','myismy')
<re.Match object; span=(0, 2), match='my'>
>>> re.match('my','myismy')
<re.Match object; span=(0, 2), match='my'>
>>> re.match('my','ismy')  # - 等同于 re.search('^my','ismy')  此处返回的是None
>>>
1
2
3
4
5
6
# re.findall()

从头到尾找到全部

>>> re.findall('dc','my name is dc, dc is cool.')
['dc', 'dc']
>>> re.findall('d(c)','my name is dc, dc is cool.')
['c', 'c']
1
2
3
4
# re.split()

相比于字符串内置的split函数更强大!!

>>> 'egon:18:male'.split(':')
['egon', '18', 'male']
>>> re.split(':','egon:18:male')
['egon', '18', 'male']
>>> re.split('[o8a:e]','egon:18:male')  # -- []里的字符都是分割符
['', 'g', 'n', '1', '', 'm', 'l', '']
1
2
3
4
5
6
# re.sub()

re.sub('old','new','string','num') 替换 注意哦! 没有改变原字符串 , 因为字符串是不可变的.

>>> re.sub('egon','dc','egon:18:male')
'dc:18:male'
# -- 'sub aaxx' ' is wxx' ' is vxx' 都被替换成了'dc'
>>> re.sub('.*?xx','dc','sub aaxx is wxx is vxx is good')
'dcdcdc is good'
>>> re.sub('[a-z]+xx','dc','sub aaxx is wxx is vxx is good')
'sub dc is dc is dc is good'
1
2
3
4
5
6
7
# re.compile()

pattern = re.compile('正则') 可以预存一个正则表达式,反复使用!

>>> pattern = re.compile('egon')
>>> pattern.findall('egon is cool.my name is egon。')
['egon', 'egon']
>>> pattern.search('egon is cool.my name is egon。')
<re.Match object; span=(0, 4), match='egon'>
1
2
3
4
5

# logging模块

logging模块包含四种角色: Logger、Filter、Formatter、Handler
Filter 过滤日志 暂时不会用.

参考链接: https://www.cnblogs.com/linhaifeng/articles/6384466.html#_label12

"""
★ --日志格式
"""
%(name)s              Logger的名字
%(levelno)s           数字形式的日志级别
%(levelname)s         文本形式的日志级别
%(pathname)s          调用日志输出函数的模块的完整路径名,可能没有
%(filename)s          调用日志输出函数的模块的文件名
%(module)s            调用日志输出函数的模块名
%(funcName)s          调用日志输出函数的函数名
%(lineno)d            调用日志输出函数的语句所在的代码行
%(created)f           当前时间,用UNIX标准表示时间,浮点数表示
%(relativeCreated)d   输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s           字符串形式的当前时间. 默认格式是 "2003-07-08 16:49:45,896".逗号后面的是毫秒
%(thread)d            线程ID,可能没有
%(threadName)s        线程名,可能没有
%(process)d           进程ID,可能没有
%(message)s           用户输出的消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 第一种方式

cmd控制台屏幕输出: 2022-09-26 14:45:01: 小明给了小红100
a1.log文件输出: 2022-09-26 14:45:01 PM - 交易日志 - INFO - 1: 小明给了小红100
a2.log文件输出: 2022-09-26 14:45:01: 小明给了小红100

# -- 1.py
import logging

"""
★ --Logger: 负责产生日志信息
"""
# -- 此处'交易日志'是Logger日志的名字
logger1 = logging.getLogger('交易日志')


"""
★ --Formatter: 控制日志格式
"""
formatter1 = logging.Formatter(
    fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S %p',
)
formatter2 = logging.Formatter(
    fmt='%(asctime)s: %(message)s',
    datefmt='%Y-%m-%d %X',
)


"""
★ --Handler: 负责日志输出的目标
"""
h1 = logging.FileHandler(filename='a1.log', encoding='utf-8')
h2 = logging.FileHandler(filename='a2.log', encoding='utf-8')
sm = logging.StreamHandler()  # -- 屏幕


"""
★ --▲绑定logger对象与handler对象
"""
logger1.addHandler(h1)
logger1.addHandler(h2)
logger1.addHandler(sm)


"""
★ --▲绑定handler对象与formatter对象
"""
h1.setFormatter(formatter1)
h2.setFormatter(formatter2)
sm.setFormatter(formatter2)

"""
★ --设置日志级别: 可以在logger与handler这两个角色处进行设置
日志级别有5档: debug -> info -> warning(默认) -> error -> critical
分别对应数字:  调试10 - 消息20 - 警告30 - 错误40 - 严重50
1> 若设置日志级别为error,那么会记录error和critical的信息
2> 若设置了`logger1.setLevel(30)`,那么`h1.setLevel(10)`是不生效的.
   handler设置的日志级别应该大于等于logger!!
"""
# -- logger1.setLevel(30)
h1.setLevel(10)

logger1.info('小明给了小红100')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 第二种方式

我们基本上都会在settings.py文件中进行日志的配置

对象 含义
logger 产生日志的对象
filter 过滤日志的对象
handler 接收日志然后控制打印到不同的地方
FileHandler用来打印到文件中,StreamHandler用来打印到终端
formatter 可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用
以此来控制不同的Handler的日志格式
# -- settings.py
import os
import logging.config

"""
★ --自定义日志输出格式
standard_format 标准格式
simple_format 简单格式
"""
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'


"""
★ --log文件的路径
"""
logfile_dir = os.path.dirname(os.path.abspath(__file__))  # -- log文件的目录
logfile_name = 'all2.log'  # -- log文件名
# 如果不存在定义的日志目录就创建一个
# if not os.path.isdir(logfile_dir):
#     os.mkdir(logfile_dir)
logfile_path = os.path.join(logfile_dir, logfile_name)  # -- 拼接log文件路径


"""
★ --logging配置字典
    ▲handler&formatter  logger&handler  
"""
LOGGING_DIC = {
    'version': 1,  # -- 版本号
    'disable_existing_loggers': False,
    'formatters': {
        # -- 注意:'standard'、'simple'是可自己定义的名字 format是固定的名字不能改
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    # -- 自定义了两个handler对象 console和default
    'handlers': {
        # -- 1> 打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # -- 'logging.StreamHandler'表明打印到屏幕
            'formatter': 'simple'              # -- 与formatter进行绑定
        },
        # -- 2> 打印到文件的日志,收集DEBUG及以上的日志
        'default1': {
            'level': 'DEBUG',                # -- 设置日志级别(第二层的关卡)
            'class': 'logging.FileHandler',  # -- logging.FileHandler表明保存到文件
            'formatter': 'standard',         # -- formatter的值是前面formatters部分中自定义的
            'filename': logfile_path,        # -- 日志文件名,准确点说是日志输出路径
            'encoding': 'utf-8',             # -- 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        # -- 默认配置
        '': {
            # -- 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'handlers': ['console', 'default1'], 
            'level': 'DEBUG',  # 设置日志级别(第一层关卡)
            'propagate': False,  # 这个配置关乎logger的继承
        },
    },
}


def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging字典配置
    # -- logging.getLogger('可指定任意的日志名')
    # -- 会去字典的loggers属性里面找,没找到就用" "默认配置
    logger = logging.getLogger('交易日志')  
    # print(logger)  # -- <Logger logger1 (DEBUG)>
    logger.debug('It debug!')
    logger.info('It works!')


if __name__ == '__main__':
    load_my_logging_cfg()
    
"""
cmd控制台输出:
[DEBUG][2022-09-26 15:57:30,103][settings.py:70]It debug!
[INFO][2022-09-26 15:57:30,104][settings.py:71]It works!

all2.log文件:
[2022-09-26 15:57:30,103][MainThread:4670004736][task_id:交易日志][settings.py:70][DEBUG][It debug!]
[2022-09-26 15:57:30,104][MainThread:4670004736][task_id:交易日志][settings.py:71][INFO][It works!]
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

# 纯享版

import logging.config
import os

base_dir = os.path.dirname(os.path.dirname(__file__))
base_db = os.path.join(base_dir, "db")
base_log = os.path.join(base_dir, "log")

"""
★ --logging日志配置
"""
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

logfile_name = 'ATM.log'
if not os.path.isdir(base_log):
    os.mkdir(base_log)
logfile_path = os.path.join(base_log, logfile_name)

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'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'formatter': 'standard',
            'filename': logfile_path,
            'encoding': 'utf-8',
        },
    },
    'loggers': {
        '': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

if __name__ == '__main__':
    def load_my_logging_cfg():
        logging.config.dictConfig(LOGGING_DIC)
        logger = logging.getLogger('交易日志')
        logger.debug('It debug!')
        logger.info('It works!')


    load_my_logging_cfg()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# hashlib模块

hash是一种算法,该算法接受传入的内容(文本文件--字符;视频文件--二进制),经过运算得到一串hash值.

主要提供SHA1 SHA224 SHA256 SHA384 SHA512 MD5算法

# hash三大特性

1> 只要传入的内容一样, 得到的hash值必然一样!
在浏览器中下载文件,最后会停顿下,就是在做hash校验,避免在网络传输过程中文件出错,保证文件完整.
2> 只要我们使用的hash算法固定,无论传入的内容有多大,得到的hash值的长度是固定的
下载的文件内容是1个T呢?是否意味着hash值也会占很大的内存?所以hash值长度需要固定.  3> 不可以用hash值逆推出原来的内容,单向的
在网络传输过程中的包可能会被截获. 若账号密码是明文的那就完蛋了. 

基于1和2可以在下载文件时做文件 一致性校验
基于1和3可以对密码进行 加密

import hashlib

# -- 1> 造出hash工厂
#       还可以是其它算法 m = hashlib.sha512()
# m = hashlib.md5('python'.encode('utf8')) 可以先传一段数据.
m = hashlib.md5()
# -- 2> 运送原材料
#       update值接收bytes类型的数据
# -- 注意:可以将一段很长的数据update多次,与一次update这段长数据,得到的结果是一样的.
m.update('hello'.encode('utf8'))
m.update('world'.encode('utf8'))
# -- 3> 产出hash值 "一串由小写字母和数字构成的字符串"
print(m.hexdigest())  # fc5e038d38a57032085441e7fe7010b0
1
2
3
4
5
6
7
8
9
10
11
12
13

# 文件一致性校验

# 原理

客户端从服务端下载一个文件, S传输文件给C的同时,会传给C一个加密该文件后得到的hash值.
传输完毕,C同样对文件进行加密,比对hash值是否相同.. 若相同则证明文件是完整的,没有被篡改过.

Q: 中间人需要如何攻击呢?
A: 它需要得到所有的数据包,(因为文件是一段一段传输的),并且篡改加入攻击代码,给一个新的hash值.
这样客户端才发觉不到异常.. 但凡某一环节出现问题,攻击都会失败..

# 实现

Q: m.update(f.read()) 文件过大可能就卡死了!
A: 一行一行的拿出来虽然解决了内存问题,但若视频文件有1个T,要循环许久许久.
     我们不应该将文件真的全读完,应采用比例的方式缩短算hash值的时间
c/s端都按照我们自定义的方式进行hash校验!!

import hashlib

m = hashlib.md5()
with open('01.mp4', 'rb') as f:
    for line in f:
        m.update(line)
print(m.hexdigest())
1
2
3
4
5
6
7

# 明文密码加密

(´▽`)没有绝对的安全哦! 有程序就有漏洞,只需提升安全等级,让破坏者的破解成本远远大于其收益.

# 原理

用户注册后, 服务端是有一份正确的用户名和对应的用哈希算法(假设是md5)加密后的密码的;
客户端用户在登录界面输入密码后,会用md5进行加密,并将用户名与加密后的密码传到服务端进行比对..

Q: 中间人需要如何攻击呢?
A: 哪怕中间人截获了C传给S的数据包,拿到加密后的hash值也反推不了密码明文.
     但中间人可以进行 撞库 (抓包 + 猜到用的hash算法 + 常用的密码字典), 蒙你的密码是多少..
     一旦密码字典中的某个value通过相同的hash算法算出的hash值与抓包截获的一致.. 密码就被破解了.

# 实现
import hashlib

pwd = input('>>>:')
m = hashlib.md5()
m.update(pwd.encode('utf-8'))
print(m.hexdigest())
1
2
3
4
5
6

解决'撞库', 办法一:使用强密码;办法二: 密码加盐.

m = hashlib.md5()  # -- 不加盐
m.update(pwd.encode('utf-8'))

# -- C/S端是知道加的这个'盐'是'天王盖地虎',但破坏者不知道!
# -- 更狠一点,加盐加在密码的某些位置..
m = hashlib.md5('天王盖地虎'.encode('utf-8'))  # -- 加盐
m.update(pwd.encode('utf-8'))
1
2
3
4
5
6
7
# 补充: hmac模块

python的 hmac模块 -- 基于密钥的消息验证.
它内部会对我们创建key和内容进行进一步的处理后再加密!

import hmac

# -- key值'小鸡炖蘑菇',就是我们加的盐 相当于强制加'盐'
m = hmac.new('小鸡炖蘑菇'.encode('utf8'), digestmod='md5') 
m.update('hello'.encode('utf8'))  # -- value
print(m.hexdigest())
1
2
3
4
5
6

# subprocess模块

执行系统命令 ,拿到结果,想咋操作就咋操作,而不是直接丢到控制台打印

import os
os.system('ls') # -- 可以执行系统命令,但执行结果直接打印到控制台上了

我们想要拿到该执行结果,咋搞?
1
2
3
4

subprocess.Popen()

第一个参数: 必须是字符串形式的命令
第二个参数: 必须写 shell=True 简单理解调用命令解释器 eg:cmd终端
第三个参数: stdout=subprocess.PIPE 命令正确输出
        PIPE 管道简单理解成内存里的一种数据结构
        命令结果只有一份, 丢到管道里暂存啦, 就不会直接在终端显示
第四个参数: stderr=subprocess.PIPE 命令错误输出
        这与命令正确输出的管道是两根不同的管道哦

import subprocess
from sys import stdout
# -- py文件的运行开启了一个父进程,运行到此行代码开启一个子进程
#    两个进程是完全隔离独立的,不能在内存中共享数据
#    但我们需要将子进程的系统命令运行结果给父进程,只能造一个两者共享的内存空间 
#    管道(进程与进程之间共享数据用的)
obj = subprocess.Popen(
    'ls',
    shell=True,
    # -- subprocess.PIPE每调用就会产生一个新的管道 
    #    'oop里面的property->看似调用的是一属性实则是一功能的执行'
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

# -- 父进程准备从正确管道读取结果,但子进程运行系统命令的结果可能还没产生呢.
#    因为进程与进程之间是相互隔离 独立的
#    若管道里没有结果,父进程就会在这等着子进程将结果丢到管道中,相当于取代了time.sleep()操作
# 注意哦! 从管道中读走这份数据后,再读数据结果为空.因为只有一份.
stdout_res = obj.stdout.read()
# -- 系统命令的运行结果是bytes类型,固定了的. 想要打印结果,需要解码 windows-gbk;mac-utf8
print(stdout_res.decode('utf-8'))
stderr_res = obj.stderr.read()
print(stderr_res.decode('utf-8'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# sys模块

sys.path 处理导入模块时的环境变量!!!

sys.argv 用于命令行执行py文件时, 接收后面跟着的参数

# -- train.py
import sys
print(sys.argv) # -- ['train.py', '参数1', '参数2']

(application) One_Piece@DCdeMacBook-Air Desktop % python train.py 参数1 参数2
['train.py', '参数1', '参数2']
1
2
3
4
5
6

实现拷贝文件的功能

命令行执行命令 python cp.py 源文件路径 目标文件路径

# -- cp.py
src_file = sys.argv[1]
dst_file = sys.argv[2]

# -- rb模式 可以打开任何文件
# -- 注意:以w的方式打开文件,未关闭的情况下往文件里写内容指针不断后移
#        中途关掉,下次再以w的形式打开,内容将被清空
with open('src_file','rb') as read_f,\
    open('dst_file','wb') as write_f:
    for line in read_f:
        write_f.write(line)
1
2
3
4
5
6
7
8
9
10
11

# json&pickle模块

# 异同

# json格式

json 跟 字典 是有区别的!!

json分为json对象和json数组
        json对象就类似于字典.
        json数组是用 [] 包起来的多个json对象
json对象可由多个键值对组成, 键必须由""包裹!!!,值可以是json对象或者是json数组.

JSON类型 Python类型
{} dict
[] list
"string" str
1234.56 int或float
true/false True/False
null None
# 序列化

可以保存程序的运行状态; 实现数据的跨平台的交互

json和pickle都可用于序列化, 序列化是将对象转换成易于传输的形式的过程.
通俗点就是将当前时刻处于内存中的数据转换一种格式.. 反序列化就是将数据转换成序列化之前的样子.

json 跨平台性强;只能支持/对应python部分的数据类型
pickle 可以支持/对应所有python的数据类型;只能被python识别,不能跨平台

# json模块的使用

# json序列化

dumps      dump

import json

dic = {'name': 'egon', 'age': 18, 'sex': 'male'}
json_str = json.dumps(dic)
# -- 序列化后,自动将字典中的单引号转换成了双引号
# print(json_str)  # -- {"name": "egon", "age": 18, "sex": "male"}
# print(type(json_str))  # -- <class 'str'>
with open('db.json', 'wt', encoding='utf-8') as f:
    f.write(json_str)


# !!-- 序列化得到json_str、把json_str写入db.json文件这两步 等同于
with open('db.json','wt',encoding='utf-8') as f:
		json.dump(dic,f)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# json反序列化

loads      load

import json

with open('db.json', 'rt', encoding='utf-8') as f:
    data = f.read()
dic_res = json.loads(data)
# print(type(data))  # -- <class 'str'>
# print(dic_res)  # -- {'name': 'egon', 'age': 18, 'sex': 'male'}
# -- {'name': 'egon', 'age': 18, 'sex': 'male'} <class 'dict'>
# print(eval(data), type(eval(data)))
print(dic_res['sex'])  # male


# !!-- 从硬盘db.json文件中读取到data、将data反序列化得到dic_res 等同于
with open('db.json','rt',encoding='utf-8') as f:
		dic_res = json.load(f)

    
"""
提一句,在代码中我们试图用eval对json对象进行了反序列化,成功了.
因为json格式的数据在内存中也是str的数据类型. eval去除了最外层的引号.
但json格式的数据长这样呢?[1,true,null] 用eval来反序列化就不行!
建议还是使用json模块的方法,别搞这些花里胡哨的.(´▽`)
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# pickle模块的使用

必须是二进制的形式读写.

import pickle

dic = {'a': 1, 'b': 2, 'c': 3}

"""
★ --序列化
"""
pkl = pickle.dumps(dic)
# -- b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x01a\x94K\x01\x8c\x01b\x94K\x02\x8c\x01c\x94K\x03u.'
# print(pkl)
# print(type(pkl))  # -- <class 'bytes'>
with open('db.kpl', 'wb') as f:
    f.write(pkl)
""" 等同于
with open('db.kpl', 'wb') as f:
    pickle.dump(pkl,f)
"""


"""
★ --反序列化
"""
with open('db.kpl', 'rb') as f:
    data = f.read()
res = pickle.loads(data)
print(res['a'])  # 1
""" 等同于
with open('db.kpl', 'rb') as f:
    res = pickle.load(f)
print(res['a'])
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

文件处理
常用模块

← 文件处理 常用模块→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式