Python学习笔记:第十五站 大宝藏

Python学习笔记


课程笔记参考B站视频: Python全栈开发教程


第十五站 大宝藏

1. 编码格式介绍

前面介绍过字符的编码格式,本章重点介绍其在文件操作中的原理。下面是几个常用字符编码格式之间的关系:

Python的解释器使用的是Unicode(内存),而.py文件在磁盘上使用UTF-8存储(外存)。要修改.py文件的编码格式,可以在第一行写:

#encoding=编码格式

然后在文件夹中找到该文件,记事本打开另存为,便可以在右下角看到其想要保存成的编码格式。

2. 文件的读写原理

文件的读写俗称"IO操作”,文件进行读、写操作的基本流程如下图(注意最后要关闭文件):

Python操作文件
打开或新建文件
读写文件
关闭资源

IO的原理依靠 队列(先进先出) 的数据结构,使得数据呈现出“数据流”的特性(注:C++进行流操作的头文件为#include<iostream.h>)。要使用Python操作磁盘中的文件,具体的过程为(见下图):编写.py文件(模块)–>解释器执行代码–>OS操作系统资源被调用–>完成磁盘读写操作。

下面介绍Python内置函数 open() ,其用于创建文件对象。在调用后,会在程序中创建一个文件对象,这个对象映射磁盘上的一个真实文件,在进行文件操作的过程中,再通过IO流(+缓冲区)将磁盘文件中的内容与程序中对象的内容进行同步。语法规则为:

file = open(filename[, mode, encoding])
# file:被创建的文件对象名称
# open:创建文件对象的函数
# filename:要创建或打开的文件名称
# mode:打开模式,默认是“只读”
# encoding:文本文件编码格式,默认是“gbk”

下面展示内置函数open()的使用方法:

  1. 创建一个a.txt文档,内容为:
去码头整点薯条
圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵
  1. 源代码:
print('----------读取磁盘中的文件----------')
file = open('E:/python_learn/a.txt', 'r', encoding='utf-8')
print(file.readlines()) # 读取所有的行
file.close()            # 关闭文件
  1. 运行结果:
['去码头整点薯条\n', '圣火昭昭 圣光耀耀 凡我弟子 喵喵喵喵']

可以看到,方法.readlines()返回一个列表,存储了文本文件中所有的信息。

3. 文件读写操作

文件打开模式描述
r以只读模式打开文件,文件指针放在文件的开头
w以只写模式打开文件,文件指针在文件的开头。
若文件不存在则创建,若文件存在则覆盖原有内容。
a以追加模式打开文件。
若文件不存在则创建,文件指针在文件开头。
若文件存在则追加内容,文件指针在文件末尾。
b以二进制方式打开文件,不能单独使用,需要与其他模式一起使用,如 rb 或 wb
+以读写方式打开文件,不能单独使用,需要与其它模式一起使用,如 a+

本节主要介绍一些常用的文件打开模式(如上图)。按文件中数据的组织形式,文件类型主要分为两类:

  1. 文本文件:存储的是普通"字符”文本,默认为unicode字符集,可以使用记本事程序打开。
  2. 二进制文件:把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件打开,如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语句体

上面的代码中,withas之间的语句称为“上下文表达式”,其结果为上下文管理器。同时,也会创建一个运行时上下文,自动调用_ _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

可支持功能有:订单 物流 账户 支付 退出
亲亲有什么问题呢?退出
亲亲再见呦~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虎慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值