Python学习笔记
课程笔记参考B站视频: Python全栈开发教程。
第十五站 大宝藏
1. 编码格式介绍
前面介绍过字符的编码格式,本章重点介绍其在文件操作中的原理。下面是几个常用字符编码格式之间的关系:
![](https://i-blog.csdnimg.cn/blog_migrate/d42ddf4b03f1c6b23e874a988367cda3.png)
Python的解释器使用的是Unicode
(内存),而.py文件
在磁盘上使用UTF-8
存储(外存)。要修改.py文件的编码格式,可以在第一行写:
#encoding=编码格式
然后在文件夹中找到该文件,记事本打开另存为,便可以在右下角看到其想要保存成的编码格式。
2. 文件的读写原理
文件的读写俗称"IO操作”,文件进行读、写操作的基本流程如下图(注意最后要关闭文件):
IO的原理依靠 队列(先进先出) 的数据结构,使得数据呈现出“数据流”的特性(注:C++进行流操作的头文件为#include<iostream.h>
)。要使用Python操作磁盘中的文件,具体的过程为(见下图):编写.py文件(模块)–>解释器执行代码–>OS操作系统资源被调用–>完成磁盘读写操作。
![](https://i-blog.csdnimg.cn/blog_migrate/ff490bb113161ccf657cf2bcee8dac1e.png)
下面介绍Python内置函数 open()
,其用于创建文件对象。在调用后,会在程序中创建一个文件对象,这个对象映射磁盘上的一个真实文件,在进行文件操作的过程中,再通过IO流(+缓冲区)将磁盘文件中的内容与程序中对象的内容进行同步。语法规则为:
file = open(filename[, mode, encoding])
# file:被创建的文件对象名称
# open:创建文件对象的函数
# filename:要创建或打开的文件名称
# mode:打开模式,默认是“只读”
# encoding:文本文件编码格式,默认是“gbk”
下面展示内置函数open()
的使用方法:
- 创建一个
a.txt
文档,内容为:
去码头整点薯条
圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵
- 源代码:
print('----------读取磁盘中的文件----------')
file = open('E:/python_learn/a.txt', 'r', encoding='utf-8')
print(file.readlines()) # 读取所有的行
file.close() # 关闭文件
- 运行结果:
['去码头整点薯条\n', '圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵']
可以看到,方法.readlines()
返回一个列表,存储了文本文件中所有的信息。
3. 文件读写操作
文件打开模式 | 描述 |
---|---|
r | 以只读模式打开文件,文件指针放在文件的开头 |
w | 以只写模式打开文件,文件指针在文件的开头。 若文件不存在则创建,若文件存在则覆盖原有内容。 |
a | 以追加模式打开文件。 若文件不存在则创建,文件指针在文件开头。 若文件存在则追加内容,文件指针在文件末尾。 |
b | 以二进制方式打开文件,不能单独使用,需要与其他模式一起使用,如 rb 或 wb |
+ | 以读写方式打开文件,不能单独使用,需要与其它模式一起使用,如 a+ |
本节主要介绍一些常用的文件打开模式(如上图)。按文件中数据的组织形式,文件类型主要分为两类:
- 文本文件:存储的是普通"字符”文本,默认为unicode字符集,可以使用记本事程序打开。
- 二进制文件:把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件打开,如mp3音频文件、jpg图片、.doc文档等。
下面是代码示例:
print('----------演示写文件的操作----------')
file = open('E:/python_learn/a.txt', 'w', encoding='utf-8')
file.write('皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!')
file.close()
file = open('E:/python_learn/a.txt', 'r', encoding='utf-8')
print(file.readlines())
file.close()
# 可以观察到程序覆盖了原文件的内容(汪汪教占据了喵喵教的据点)
# 注:若把文件名称更改为一个没有的文件,那么可以观察到程序创建了一个新的文件
print('----------演示追加模式的操作----------')
file = open('E:/python_learn/a.txt', 'a', encoding='utf-8')
file.write('汪汪汪汪!')
file.close()
file = open('E:/python_learn/a.txt', 'r', encoding='utf-8')
print(file.readlines())
file.close()
print('----------演示读取二进制文件的操作----------')
png_file = open('E:/python_learn/demo.png', 'rb')
copy_png = open('E:/python_learn/demo_copy.png', 'wb')
copy_png.write(png_file.read()) # 边读边写,复制图片到指定位置
png_file.close()
copy_png.close()
print('图片复制完成!')
# 运行后可以在相应路径下看到复制好的图片
# 注:追加二进制文件可以看到几幅图片并列排放,很有意思
print('----------演示同时读写文件的操作----------')
file = open('E:/python_learn/a.txt', 'w+', encoding='utf-8')
print(file.readlines())
file.write('喵喵喵喵!')
print(file.readlines())
file.close()
# a+可以同时读写,但注意文件存在文件指针在文件末尾,所以读不出东西
# r+可以同时读写,但注意文件指针在文件开头,写进去文本之后,只会读出文本后面的内容
# w+可以同时读写,但注意文件存在会覆盖原内容,一开始是读不出内容的
- 运行结果
----------演示写文件的操作----------
['皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!']
----------演示追加模式的操作----------
['皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!汪汪汪汪!']
----------演示读取二进制文件的操作----------
图片复制完成!
----------演示同时读写文件的操作----------
[]
[]
文件读写操作的关键在于文件指针的位置!
4. 文件对象常用的方法
方法名 | 说明 |
---|---|
.read([size]) | 从文件中读取size个字节或字符的内容返回。若省略[size],则读取到文件末尾,即一次读取文件所有内容。 |
.readline() | 从文本文件中读取完本行所有内容。 |
.readlines() | 把文本文件中每一行都作为独立的字符串对象,并将这些对象放入列表返回。 |
.write(str) | 将字符串str内容写入文件 |
.writelines(s_list) | 将字符串列表s_ list写入文本文件,不添加换行符。 |
.seek(offset[,whence]) | 把文件指针移动到新的位置(单位:字节)。 offset:相对于whence的位置。正数向文件尾移动,负数向文件头移动。 whence:计算位置的起点。0表示文件头(默认)、1表示当前位置,2表示文件尾。 注:非b模式的打开的文件,只允许从文件头开始计算相对位置。 |
.tell() | 返回文件指针的当前位置(单位:字节)。 |
.flush() | 把缓冲区的内容写入文件,但不关闭文件。 |
.close() | 把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源。 |
注:二进制模式可以打开文本文件,但是读出来的内容就不会自动转换成字符,而是以16进制数呈现。
注:.flush()
的应用场景为:其他进程要读取该文件,本进程又不想关闭,于是刷新,其他进程读取到的就是最新的文件了。.flush()
相当于保存,.close()
相当于关闭。
下面是代码示例:
- 将文本文件恢复成:
去码头整点薯条
圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵
皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!
- 源代码:
file = open('E:/python_learn/a.txt', 'r+', encoding='utf-8')
print(file.read(3)) # 只读取3个字符
print('文件指针当前位置为:', file.tell()) # 因为汉字有3个字节
print(file.readline()) # 读取完本行,注意有个换行符
file.seek(3, 0) # 非二进制模式打开,只能从文件头计算位置
print(file.readlines()) # 读取剩下的行,并做成列表
# 注意此时文件指针在文件尾
file.writelines(['\n喵喵喵喵\n','汪汪汪汪'])
# 文件指针还是在文件尾
file.seek(file.tell()-3*8-2) # 将指针向前移动,\n占用2个字节
print(file.readline()) # 读取完本行
file.close() # 关闭文件
- 运行结果
去码头
文件指针当前位置为: 9
整点薯条
['码头整点薯条\n', '圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵\n', '皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!']
喵喵喵喵
- 文本文件变为:
去码头整点薯条
圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵
皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!
喵喵喵喵
汪汪汪汪
5. with语句(上下文管理器)
with语句可以自动管理上下文资源,不论什么原因跳出with块
,都能确保文件正确的关闭(不用再手动写.close()
函数),以此来达到释放资源的目的。调用语法为:
with open('文件路径', '文件打开模式') [as 文件对象名称:]
with语句体
上面的代码中,with
和as
之间的语句称为“上下文表达式”,其结果为上下文管理器。同时,也会创建一个运行时上下文,自动调用_ _enter_ _()
方法,并将返回值赋给as
后面声明的文件对象。离开运行时上下文,自动调用上下文管理器的特殊方法_ _exit_ _()
。
注:若一个类对象实现了_ _enter_ _()
方法和_ _exit_ _()
方法,那这个类对象就遵守了上下文管理协议,这个类对象的实例对象就称为上下文管理器。
下面是 创建上下文管理器对象 的代码示例:
class MyContentMgr(object):
def __enter__(self):
print('enter方法被调用了')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit方法被调用了')
def show(self):
# print('show方法被调用了')
print('show方法被调用了', 1.0/0)
with MyContentMgr() as file: # 等价于file = MycontentMgr()
file.show()
- 下面执行show()方法中的正确语句,其运行结果为:
enter方法被调用了
show方法被调用了
exit方法被调用了
- 即使执行过程中报错,也会自动调用
_ _exit_ _()
函数回收内存,下面是运行结果:
enter方法被调用了
exit方法被调用了
Traceback (most recent call last):
File "E:/python_learn/proiect1/chp15/demo5.py", line 16, in <module>
file.show()
File "E:/python_learn/proiect1/chp15/demo5.py", line 13, in show
print('show方法被调用了', 1.0/0)
ZeroDivisionError: float division by zero
下面是 正常演示with
语句 的代码示例:
with open('E:/python_learn/a.txt','r',encoding='utf-8') as file:
print(file.read())
# 不用再手动写.close()函数,免得忘记写造成文件资源的浪费
- 运行结果
去码头整点薯条
圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵
皓月苍苍,皓月朗朗,凡我教徒,汪汪汪汪!
喵喵喵喵
汪汪汪汪
以后进行文件操作时,建议都放进with语句块中。
比如下面这三行语句实现的文件的复制:
'''使用with语句实现文件的复制'''
with open('E:/python_learn/a.txt', 'r', encoding='utf-8') as file:
copy_file = open('E:/python_learn/a_copy.txt', 'w', encoding='utf-8')
copy_file.write(file.read())
运行之后就可以在相应的文件夹中看到复制的文本文件。
6. 目录操作
os
模块 是Python内置的与操作系统功能和文件系统相关的模块,该模块中的语句的执行结果通常与操作系统有关,在不同的操作系统上运行,得到的结果可能不一样。 os
模块 与 os.path
模块 用于对目录或文件进行操作。该模块属于Python自带的一个模块,不需要下载可以直接导入。
下面是代码示例:
import os
'''打开记事本'''
os.system('notepad.exe') # 与win+r --> notepad等价
'''打开计算器'''
os.system('calc.exe')
'''直接调用可执行文件'''
os.startfile('D:\\Program Files\\MATLAB\\R2022a\\bin\\matlab.exe') # 注意转义字符的问题,我的matlab路径
- 运行后会依次启动记事本、计算器、matlab。
下表是 os
模块 中对于目录的一些操作函数及其说明:
os.模块函数 | 说明 |
---|---|
.getcwd() | 返回当前的工作目录 |
.listdir(path) | 返回指定路径下的文件和目录信息 |
.mkdir(path[,mode]) | 创建目录 |
.makedirs(path1/path2...[,mode]) | 创建多级目录 |
.rmdir(path) | 删除一个空目录 |
.removedirs(path1/path2...) | 删除多级目录,最后一个必须为空目录。 若整个目录为空,则删除整个目录; 否则从最底层目录向上删除,直到不是空目录为止。 |
.chdir(path) | 将path设置为当前工作目录 |
.walk(path) | 遍历所有文件,产生3-元组(dirpath,dirnames,filenames)。 菜鸟教程 Python3 os.walk()方法 |
下面是代码示例:
import os
print('-----------演示os.getcwd-----------')
print('当前文件所在目录:', os.getcwd())
print('-----------演示os.listdir-----------')
print('指定目录下包含:', os.listdir()) # 不写路径就默认是当前文件夹
print('-----------演示os.mkdir-----------')
os.mkdir('test')
print('创建单级目录后包含:', os.listdir())
print('-----------演示os.makedirs-----------')
os.makedirs('test/dir1/dir2')
print('多级目录中包含:', os.listdir('test/dir1'))
print('-----------演示os.removedirs-----------')
os.removedirs('test/dir1/dir2')
print('删除多级目录后:', os.listdir())
print('-----------演示os.chdir-----------')
os.chdir('../chp14')
print('当前文件所在目录:', os.getcwd())
# os.walk()方法放在本节的最后演示。
# help(os.mkdir)
- 运行结果
-----------演示os.getcwd-----------
当前文件所在目录: E:\python_learn\proiect1\chp15
-----------演示os.listdir-----------
指定目录下包含: ['demo1.py', 'demo2.py', 'demo3.py', 'demo4.py', 'demo5.py', 'demo6.py', 'demo7.py', 'demo8.py', 'demo9.py']
-----------演示os.mkdir-----------
创建单级目录后包含: ['demo1.py', 'demo2.py', 'demo3.py', 'demo4.py', 'demo5.py', 'demo6.py', 'demo7.py', 'demo8.py', 'demo9.py', 'test']
-----------演示os.makedirs-----------
多级目录中包含: ['dir2']
-----------演示os.removedirs-----------
删除多级目录后: ['demo1.py', 'demo2.py', 'demo3.py', 'demo4.py', 'demo5.py', 'demo6.py', 'demo7.py', 'demo8.py', 'demo9.py']
-----------演示os.chdir-----------
当前文件所在目录: E:\python_learn\proiect1\chp14
下表是 os.path
模块 中对于目录的一些操作函数及其说明:
os.path模块函数 | 说明 |
---|---|
.abspath(path) | 用于获取文件或目录的绝对路径 |
.exists(path) | 用于判断文件或目录是否存在,如果存在返回True,否则返回False |
.join(path,name) | 将目录与目录或者文件名拼接起来 |
.split() | 分离目录、文件扩展名,返回元组 |
.splitext() | 分离文件名、扩展名,返回元组 |
.basename(path) | 从一个目录中提取文件名 |
.dirname(path) | 从一个路径中提取文件路径,不包括文件名 |
.isdir(path) | 用于判断是否为路径 |
下面是代码示例:
print('-----------演示os.path.abspath-------------')
print(os.path.abspath('demo9.py'))
print('-----------演示os.path.exists--------------')
print(os.path.exists('demo9.py'), os.path.exists('demo20.py'))
print('-----------演示os.path.join----------------')
str = os.path.join(os.getcwd(), 'demo9.py') # 将路径拼成一个字符串
print(str)
print('-------演示os.path.split/splitext----------')
print(os.path.split(str))
print(os.path.splitext(str))
print('------演示os.path.basename/dirname---------')
print(os.path.basename(str))
print(os.path.dirname(str))
print('-----------演示os.path.isdir---------------')
print('\''+str+'\'',' is path? ', os.path.isdir(str))
print('\''+os.getcwd()+'\'','is path? ', os.path.isdir(os.getcwd()))
- 运行结果
-----------演示os.path.abspath-------------
E:\python_learn\proiect1\chp15\demo9.py
-----------演示os.path.exists--------------
True False
-----------演示os.path.join----------------
E:\python_learn\proiect1\chp15\demo9.py
-------演示os.path.split/splitext----------
('E:\\python_learn\\proiect1\\chp15', 'demo9.py')
('E:\\python_learn\\proiect1\\chp15\\demo9', '.py')
------演示os.path.basename/dirname---------
demo9.py
E:\python_learn\proiect1\chp15
-----------演示os.path.isdir---------------
'E:\python_learn\proiect1\chp15\demo9.py' is path? False
'E:\python_learn\proiect1\chp15' is path? True
针对上述os
模块和os.path
模块的函数,完成课堂实例:列出当前目录(工作目录)下所有的.py
文件。
下面是代码示例:
'''课堂案例:列出指定目录下的所有py文件'''
import os
import os.path
dir_list = os.listdir()
print('当前目录下包含:\n', dir_list)
print('其中.py文件有:')
for item in dir_list:
exte_name = os.path.splitext(item)[1]
if exte_name == '.py':
print(item)
'''-----------老师的方法------------'''
# import os
# lst = os.listdir()
# for filename in lst:
# if filename.endswith('.py'):
# print(filename)
# # 注:亮点是使用了字符串的endswidth方法
- 运行结果
当前目录下包含:
['demo1.py', 'demo10.py', 'demo2.py', 'demo3.py', 'demo4.py', 'demo5.py', 'demo6.py', 'demo7.py', 'demo8.py', 'demo9.py', '新建 文本文档.txt', '新建文件夹']
其中.py文件有:
demo1.py
demo10.py
demo2.py
demo3.py
demo4.py
demo5.py
demo6.py
demo7.py
demo8.py
demo9.py
在最后,来演示一下os.walk()方法。os.walk()方法在办公自动化中最常用到,非常重要,所以单独演示。
下面是代码示例:
'''演示os.walk()的使用方法'''
import os
import os.path
path = os.getcwd()
lst_files = os.walk(path)
print('-----------1.依次输出每个目录下的文件属性-----------')
for dirpaths,dirnames,filenames in lst_files:
print('-----------------------------')
print(dirpaths)
print(dirnames)
print(filenames)
print('-----------2.输出当前目录下所有目录及文件的完整路径--------')
lst_files = os.walk(path)
for dirpaths2,dirnames2,filenames2 in lst_files:
print('-----------------------------')
for i in dirnames2:
print(os.path.join(dirpaths2, i))
for i in filenames2:
print(os.path.join(dirpaths2, i))
- 运行结果
-----------1.依次输出每个目录下的文件属性-----------
-----------------------------
E:\python_learn\proiect1\chp15
['新建文件夹']
['demo1.py', 'demo10.py', 'demo11.py', 'demo2.py', 'demo3.py', 'demo4.py', 'demo5.py', 'demo6.py', 'demo7.py', 'demo8.py', 'demo9.py', '新建 文本文档.txt']
-----------------------------
E:\python_learn\proiect1\chp15\新建文件夹
[]
['新建 RTF 文件.rtf']
-----------2.输出当前目录下所有目录及文件的完整路径--------
-----------------------------
E:\python_learn\proiect1\chp15\新建文件夹
E:\python_learn\proiect1\chp15\demo1.py
E:\python_learn\proiect1\chp15\demo10.py
E:\python_learn\proiect1\chp15\demo11.py
E:\python_learn\proiect1\chp15\demo2.py
E:\python_learn\proiect1\chp15\demo3.py
E:\python_learn\proiect1\chp15\demo4.py
E:\python_learn\proiect1\chp15\demo5.py
E:\python_learn\proiect1\chp15\demo6.py
E:\python_learn\proiect1\chp15\demo7.py
E:\python_learn\proiect1\chp15\demo8.py
E:\python_learn\proiect1\chp15\demo9.py
E:\python_learn\proiect1\chp15\新建 文本文档.txt
-----------------------------
E:\python_learn\proiect1\chp15\新建文件夹\新建 RTF 文件.rtf
注意,迭代器对象只能使用一次,后续再执行一次,结果就会为空。所以,上面的代码更好的写法是直接将.walk()语句放进for循环中,如:
for dirpaths,dirnames,filenames in os.walk(path):
7. 本章作业
1. 记录用户登录日志
下面是代码示例:
import time
import os.path
logname = 'loginfo.txt'
# 记录日志
def write_loginfo(username):
with open(logname, 'a', encoding='utf-8') as wfile:
datetime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
wfile.write(f'用户名:{username},登录时间:{datetime}\n')
# 读取日志
def read_loginfo():
if os.path.exists(logname):
with open(logname, 'r', encoding='utf-8') as rfile:
loginfo = rfile.readlines()
if not loginfo:
print('还没有用户登录过系统!')
else:
for i in loginfo:
print(i, end='')
else:
print('还没有用户登录过系统!')
if __name__ == '__main__':
username = input('请输入用户名:')
userpwd = input('请输入密码:')
# if (username=='admin' and userpwd=='admin') or (username=='user' and userpwd=='user'):
if username==userpwd:
print('登录成功!')
write_loginfo(username)
while True:
con_judge = input('0.退出 1.查看登录日志 ')
if con_judge!='0' and con_judge!='1':
continue
else:
break
if con_judge=='1':
read_loginfo()
else:
print('用户名和密码不匹配,登陆失败!')
- 运行结果
请输入用户名:lala
请输入密码:lala
登录成功!
0.退出 1.查看登录日志 1
用户名:admin,登录时间:2022-07-17 15:04:06
用户名:admin,登录时间:2022-07-17 15:04:55
用户名:admin,登录时间:2022-07-17 15:06:14
用户名:admin,登录时间:2022-07-17 15:06:42
用户名:user,登录时间:2022-07-17 15:08:54
用户名:root,登录时间:2022-07-17 15:09:41
用户名:root,登录时间:2022-07-17 15:09:58
用户名:lala,登录时间:2022-07-17 15:11:27
2. 模拟淘宝客服自动回复
下面是代码示例:
replyfile = 'reply.txt'
# 创建一个话术文件
def write_replyfile():
str_reply = '''订单|如果您有任何订单问题,可以登录淘宝账号,点击“我的订单”,查看订单详情
物流|如果您有任何物流问题,可以登录淘宝账号,点击“我的订单”,查看商品物流
账户|如果您有任何账户问题,可以联系淘宝客服,电话: XXXX - XXXXXX
支付|如果您有任何支付问题,可以联系支付宝客服, QQ: xxxxxxx'''
with open(replyfile, 'w', encoding='utf-8') as wfile:
wfile.write(str_reply)
# 在reply文件中查找相应的功能
def find_answer(qusetion):
with open(replyfile, 'r', encoding='utf-8') as rfile:
while True:
str = rfile.readline()
if not str:
print('亲亲,小蜜暂不支持您想要的功能哦~\n')
break
else:
lst_str = str.split('|')
if qusetion==lst_str[0]:
print(lst_str[1].rstrip('\n') + '\n')
return
if __name__ == '__main__':
write_replyfile()
while True:
print('可支持功能有:订单 物流 账户 支付 退出')
inword = input('亲亲有什么问题呢?')
if inword=='退出':
print('亲亲再见呦~')
break
else:
find_answer(inword)
- 运行结果
可支持功能有:订单 物流 账户 支付 退出
亲亲有什么问题呢?账户
如果您有任何账户问题,可以联系淘宝客服,电话: XXXX - XXXXXX
可支持功能有:订单 物流 账户 支付 退出
亲亲有什么问题呢?退出
亲亲再见呦~