第十一章 文件及IO操作
文件的概述及基本操作步骤
文件:
- 存储在计算机的存储设备中的一组数据序列就是文件
- 不同类型的文件通过后缀名进行区分
文本文件:由于编码格式的不同,所占磁盘空间的字节数不同(例如GBK编码格式中一个中文字符占2字节,UTF-8编码格式中一个中文字符占3字节)。
二进制文件:没有统一的编码,文件直接由0或1组成,需要使用指定的软件才能打开。
Python操作文件的步骤:
1、打开文件
变量名 = open(filename,mode,encoding)
filename:要打开的文件路径,若文件不存在则会创建文件,并不会报错。
mode:打开文件的模式。只读®,只写(w),读写(r+)
encoding:编码格式。
2、操作文件
变量名.read()
变量名.write(s) # s 表示字符串格式,其他格式不行
3、关闭文件
变量名.close()
# 学习过函数后,要习惯用函数编程
# 写文件
def my_write():
# (1)(创建)打开文件
file = open('a.txt', 'w', encoding='utf-8')
# (2)操作文件
file.write('伟大的中国梦')
# (3)关闭
file.close()
# 读取文件
def my_read():
# (1)(创建)打开文件
file = open('a.txt', 'r', encoding='utf-8')
# (2)操作文件
s = file.read()
print(type(s), s)
# (3)关闭
file.close()
# 主程序运行
if __name__ == '__main__':
my_write() # 调用函数
my_read()
文件的写入操作
文件的状态和操作过程:
文件的打开模式:
文件的打开模式 | 模式说明 |
---|---|
r | 以只读模式打开,文件指针在文件的开头,如果文件不存在,程序抛异常 |
rb | 以只读模式打开二进制文件,如图片文件 |
w | 覆盖写模式,文件不存在创建,文件存在则内容覆盖 |
wb | 覆盖写模式写入二进制数据,文件不存在则创建,文件存在则覆盖 |
a | 追加写模式,文件不存在创建,文件存在,则在文件最后追加内容 |
+ | 与 w / r / a 等一同使用,在原功能的基础上增加同时读写功能 |
文件的读写方法:
读写方法 | 描述说明 |
---|---|
file.read(size) | 从文件中读取size个字符或字节,如果没有给定参数,则读取文件中的全部内容 |
file.readline(size) | 读取文件中的一行数据,如果给定参数,则为读取这一行中的size个字符或字节 |
file.readlines() | 从文件中读取所有内容,结果为列表类型 |
file.write(s) | 将字符串s写入文件 |
file.writelines(lst) | 将内容全部为字符串的列表lst写入文件 |
file.seek(offset) | 改变当前文件操作指针的位置,英文占一个字节,中文GBK编码占两个字节,UTF-8编码占三个字节 |
def my_write(s):
# (1)打开(创建)文件
file = open('b.txt', 'a', encoding='utf-8')
# (2)写入内容
file.write(s)
file.write('\n') # 换行
# (3)关闭
file.close()
# file.writelines(lst) 将内容全部为字符串的列表lst写入文件 |
def my_write_lst(file, lst):
# (1)打开文件
f = open(file, 'a', encoding='utf-8')
# (2)操作文件
f.writelines(lst)
# (3)关闭
f.close()
if __name__ == '__main__':
my_write('伟大的中国梦')
my_write('北京欢迎你')
# 准备数据
lst = ['姓名\t', '年龄\t', '成绩\n', '张三\t', '30\t', '98'] # 要写入的列表里面的内容必须全部是字符串
my_write_lst('c.txt', lst)
文件的读取操作
def my_read(filename):
# (1)打开文件
file = open(filename, 'w+', encoding='utf-8')
# (2)操作
file.write('你好啊') # 写入完成后文件的指针在最后
# file.seek(offset) 修改文件指针的位置
file.seek(0)
# 读取
# s = file.read() # 读取全部内容
# s = file.read(2) # 2指 2个字符
# s = file.readline() # 读取一行数据
# s = file.readline(2) # 读取一行中的2个字符
# s = file.readlines() # 逐行读取全部,一行为列表中的一个元素,s是列表类型
# 读取 “好啊”
file.seek(3) # 3个字节,一个中文占三个字节,utf-8
s = file.read() # 读取全部(从指针处开始,读取指针后面的全部内容)
print(type(s), s)
# (3)关闭
file.close()
if __name__ == '__main__':
my_read('d.txt')
文件复制
学习了文件的写入和读取操作后便可以实现文件复制。文件复制实际上就是读和写操作,而且是边读边写。
def copy(src, new_path):
# 文件的复制就是边读边写操作
# (1)打开源文件
file1 = open(src, 'rb')
# (2)打开目标文件
file2 = open(new_path, 'wb')
# (3)开始复制,边读边写
s = file1.read() # 源文件读取所有
file2.write(s) # 向目标文件写入所有
# (4)关闭-->先打开的后关,后打开的先关。
file2.close()
file1.close()
if __name__ == '__main__':
src = './google.jpg' # . 代表的是当前目录
new_path = '../chap1/copy_google.jpg' # .. 表示的是上级目录,相当于windows后退
copy(src, new_path)
print('文件复制完毕')
这里需要注意的问题:
- 在关闭多个文件时,先打开的后关,后打开的先关。
- 写文件路径时 . 与 .. 的使用
with语句的使用
with语句:
又称上下文管理器,在处理文件时,无论是否产生异常,都能保证with语句执行完毕后关闭已经打开的文件,这个过程是自动的,无需手动操作。
语法结构:
with open(…) as file:
pass
注意:
with语句是可以嵌套的。例子见下方代码中的文件复制 copy() 。
def write_fun():
with open('aa.txt', 'w', encoding='utf-8') as file:
file.write('2022北京冬奥会欢迎你')
def read_fun():
with open('aa.txt', 'r', encoding='utf-8') as file:
print(file.read())
def copy(src_file, target_file):
# 复制--->先读后写
with open(src_file, 'r', encoding='utf-8') as file:
with open(target_file, 'w', encoding='utf-8') as file2:
file2.write(file.read()) # 将读取的内容直接写进文件
if __name__ == '__main__':
write_fun()
read_fun()
# 文件复制
copy('./aa.txt', './dd.txt')
一维数据和二维数据的存储与读取
数据的组织维度:
也称为数据的组织方式或存储方式,在Python中常用的数据组织方式可分为一维数据、二维数据和高维数据。
- 一维数据:通常采用线性方式组织数据,一般使用Python中的列表、元组或者集合进行存储数据。
- 二维数据:二维数据也称表格数据,由行和列组成,类似于Excel表格,在Python中使用二维列表进行存储。
- 高维数据:高维数据则是使用Key-Value方式进行组织数据,在Python中使用字典进行存储数据。在Python中内置的 json 模块专门用于处理JSON(JavaScript Object Notation)格式的数据。
# 存储和读取一维数据
def my_write():
# 一维数据,可以使用列表,元组或集合
lst = ['张三', '李四', '王五', '陈六', '麻七']
# csv:逗号分隔值,数据之间使用英文的逗号进行分隔。
with open('student.csv', 'w', encoding='utf-8') as file:
file.write(','.join(lst)) # 将列表转成字符串
def my_read():
with open('student.csv', 'r', encoding='utf-8') as file:
s = file.read()
lst = s.split(',') # 字符串分割
print(lst)
# 存储和读取二维数据(表格数据)--->使用二维列表进行存储
def my_write_table():
lst = [
['商品名称', '单价', '采购数据'],
['水杯', '98.5', '20'],
['鼠标', '89', '100']
]
with open('table.csv', 'w', encoding='utf-8') as file:
for item in lst: # item的数据类型是列表
line = ','.join(item)
file.write(line)
file.write('\n') # 换行
def my_read_table():
data = [] # 存储读取的数据
with open('table.csv', 'r', encoding='utf-8') as file:
lst = file.readlines() # 逐行读取全部内容,每一行是列表中的一个元素
# print(type(lst), lst) #<class 'list'> ['商品名称,单价,采购数据\n', '水杯,98.5,20\n', '鼠标,89,100\n']
for item in lst: # item是字符串类型
# 字符串切片--->将末尾的 \n 切掉
new_lst = item[:len(item) - 1].split(',') # 结果是列表
data.append(new_lst)
print(data)
if __name__ == '__main__':
my_write()
my_read()
my_write_table()
my_read_table()
注意上述代码中提到的 .csv 格式的文件:
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。【csv:逗号分隔值,数据之间使用英文的逗号进行分隔。】
高维数据的存储和读取
- 高维数据:高维数据则是使用Key-Value方式进行组织数据,在Python中使用字典进行存储数据。在Python中内置的 json 模块专门用于处理JSON(JavaScript Object Notation)格式的数据。
json模块的常用函数:
函数名称 | 描述说明 |
---|---|
json.dumps(obj) | 将Python数据类型转成JSON格式过程,编码过程 |
json.loads(s) | 将JSON格式字符串转成Python数据类型,解码过程 |
json.dump(obj,file) | 与dumps()功能相同,将转换结果存储到文件file中 |
json.load(file) | 与loads()功能相同,从文件file中读入数据 |
import json
# 准备高维数据
lst = [
{'name': '张三', 'age': 18, 'score': 90},
{'name': '李四', 'age': 20, 'score': 99},
{'name': '王五', 'age': 23, 'score': 89}
]
# 编码
# ensure_ascii-->正常显示中文。indent-->增加数据的缩进,为了美观,使得JSON格式的字符串更具有可读性
s = json.dumps(lst, ensure_ascii=False, indent=4)
print(type(s)) # 编码 list-->str,列表中是字典
print(s)
# 解码
lst2 = json.loads(s)
print(type(lst2))
print(lst2)
# 编码到文件中
with open('student.txt', 'w', encoding='utf-8') as file:
json.dump(lst, file, ensure_ascii=False, indent=4)
# 解码到程序中
with open('student.txt', 'r', encoding='utf-8') as file:
lst3 = json.load(file) # 直接是列表类型
print(type(lst3))
print(lst3)
os模块中常用的函数
os模块:
Python内置的与操作系统文件相关的模块,该模块中语句的执行结果通常与操作系统有关,即有些函数的运行效果在Windows操作系统和MacOS系统中不一样。
目录与文件的相关操作:
函数名称 | 描述说明 |
---|---|
getcwd() | 获取当前的工作路径 |
listdir(path) | 获取path路径下的文件和目录信息,如果没有指定path,则获取当前路径下的文件和目录信息 |
mkdir(path) | 在指定路径下创建目录(文件夹) |
makedirs(path) | 创建多级目录 |
rmdir(path) | 删除path下的空目录(只能删除空目录,若目录下有内容则不能删除) |
removedirs(path) | 删除多级目录 |
chdir(path) | 把path设置为当前工作路径(rh指的是change) |
walk(path) | 遍历目录树,结果为元组,包含所有路径名、所有目录列表和文件列表 |
remove(path) | 删除path指定的文件 |
rename(old,new) | 将old重命名为new |
stat(path) | 获取path指定的文件信息 |
startfile(path) | 启动path指定的文件 |
import os
print('当前的工作路径:', os.getcwd())
lst = os.listdir()
print('当前路径下的所有目录及文件:', lst)
print('指定路径下的所有目录及文件:', os.listdir('D:/Python'))
# 创建目录
os.mkdir('study') # 如果要创建的文件夹存在,则程序报错FileExistsError
os.makedirs('./aa/bb/cc') # 创建多级目录
# 删除目录
os.rmdir('./study') # 如果要删除的目录不存在,则程序报错FileNotFoundError
os.removedirs('./aa/bb/cc') # 删除多级目录
# 改变当前的工作路径
# os.chdir('D:/Python')
# print('当前的工作路径:', os.getcwd()) # 再写代码,工作路径就是D:/Python
# 遍历目录树--->递归操作
for dirs, dirlst, filelst in os.walk('D:\Python\demo\pythonProject'):
print(dirs)
print(dirlst)
print(filelst)
print('-' * 50)
# 删除文件
os.remove('./a.txt') # 如果要删除的文件不存在,程序报错FileNotFoundError
# 重命名
os.rename('./aa.txt', './newaa.txt')
# 转换时间格式
import time
def date_format(longtime):
s = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(longtime))
return s
# 获取文件信息
info = os.stat('./newaa.txt')
print(type(info))
print(info)
print('最近一次访问时间:', date_format(info.st_atime))
print('在Windows操作系统中显示的文件的创建时间:', date_format(info.st_ctime))
print('最后一次修改时间:', date_format(info.st_mtime))
print('文件的大小(单位是字节):', info.st_size)
# 启动路径下的文件
os.startfile('calc.exe') # 打开计算器
os.startfile(r'C:\Users\酷电编程\AppData\Local\Programs\Python\Python38\python.exe')
关于Python中文件路径问题的说明:
import os
os.startfile(r'C:\Users\酷电编程\AppData\Local\Programs\Python\Python38\python.exe') # 字符串前加r
os.startfile('C:/Users/酷电编程/AppData/Local/Programs/Python/Python38/python.exe') # 使用 /
os.startfile('C:\\Users\\酷电编程\\AppData\\Local\\Programs\\Python\\Python38\\python.exe') # 使用双反斜杠 \\
'''
说明(有关文件路径的问题):
在Windows操作系统下,在Python中有时表示文件路径时用 \ 没有问题,
但有时则会出错,因为在Python中 \ 是转义字符,因此当出错时可以用双反斜杠\\进行再转义。
另外也可以在字符串前面加 r ,r的作用是去除转义字符,即忽略该字符串中的所有转义字符,视为正常字符。
同时,以 r 开头的字符串常用于正则表达式,对应者re模块。
最好的方式是使用 / ,这种不会出错。
'''
os.path子模块中常用的函数
os.path模块:
是os模块的子模块,也提供了一些目录或文件的操作函数。
函数名称 | 描述说明 |
---|---|
abspath(path) | 获取目录或文件的绝对路径(abs:绝对值) |
exists(path) | 判断目录或文件在磁盘上是否存在,结果为bool类型,如果目录或文件在磁盘上存在,结果为True,否则为False |
join(path,name) | 将目录与目录名或文件名进行拼接,相当于字符串的“+”操作 |
splitext() | 分别获取文件名和后缀名 |
basename(path) | 从path中提取文件名 |
dirname(path) | 从path中提取路径(不包含文件名) |
isdir(path) | 判断path是否是有效路径(路径不包含文件名) |
isfile(path) | 判断file是否是有效文件 |
import os.path
print('获取目录或文件的绝对路径:', os.path.abspath('./b.txt'))
print('判断目录或文件在磁盘上是否存在:', os.path.exists('b.txt')) # 相对路径 True
print('判断目录或文件在磁盘上是否存在:', os.path.exists('newb.txt')) # False
print('判断目录或文件在磁盘上是否存在:', os.path.exists('./好好学习')) # False
print('拼接路径:', os.path.join(r'D:\Python\demo\pythonProject\chap', 'b.txt'))
print('分割文件名和文件后缀名:', os.path.split('b.txt')) # 元组类型
# 提取路径与提取文件名经常一起使用
print('提取文件名:', os.path.basename(r'D:\Python\demo\pythonProject\chap\b.txt')) # b.txt
print('提取路径:', os.path.dirname(r'D:\Python\demo\pythonProject\chap\b.txt')) # D:\Python\demo\pythonProject\chap
# 注意:路径不包括文件名
print('判断一个路径是否是有效路径:', os.path.isdir(r'D:\Python\demo\pythonProject\chap')) # True
print('判断一个路径是否是有效路径:', os.path.isdir(r'D:\Python\demo\pythonProject\chap2')) # False
print('判断一个文件是否是有效文件:', os.path.isfile(r'D:\Python\demo\pythonProject\chap\b.txt')) # True
print('判断一个文件是否是有效文件:', os.path.isfile(r'D:\Python\demo\pythonProject\chap\bbb.txt')) # False
章节习题
file = open('a.txt', 'w+', encoding='utf-8')
lst = ['北京', '上海', '天津']
file.writelines(lst)
file.seek(0)
for item in file:
print(item)
file.close()
练习题
练习一
批量创建文件。
需求:
在指定路径下批量创建3000份文本文件,文件名格式为序号_物资类别_用户识别码组成。
1)序号从0001到3000
2)物资类别包括:水果、烟酒、粮油、肉蛋、蔬菜
3)用户识别码为9位的随机十六进制数码
import random
import os
import os.path
# 函数式编程
def create_filename():
filename_lst = []
lst = ['水果', '烟酒', '粮油', '肉蛋', '蔬菜'] # 物资的类别
code = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
for i in range(1, 3001):
filename = ''
# 拼接文件名--->1、序号
if i < 10:
filename += '000' + str(i)
elif i < 100:
filename += '00' + str(i)
elif i < 1000:
filename += '0' + str(i)
else:
filename += str(i)
# 拼接文件名--->2、物资类别
filename += '_' + random.choice(lst)
# 拼接文件名--->3、识别码
s = ''
for i in range(9):
s += random.choice(code)
filename += '_' + s
filename_lst.append(filename)
return filename_lst
# 创建文件的函数
def create_file(filename):
with open(filename, 'w') as file:
pass
if __name__ == '__main__':
# 在指定的路径下创建文件
path = './data'
if not os.path.exists(path):
os.mkdir(path)
# 开始创建文件
lst = create_filename() # 调用获取文件名函数
for item in lst:
create_file(os.path.join(path, item) + '.txt')
# print(create_filename())
练习二
批量创建文件夹。
需求:
在指定路径newdir下批量创建指定个数的目录(文件夹),如果newdir目录不存在,则创建。
import os
import os.path
def mkdirs(path, num):
for item in range(1, num + 1):
os.mkdir(path + '/' + str(item))
if __name__ == '__main__':
path = './newdir'
if not os.path.exists(path):
os.mkdir(path)
num = eval(input('请输入要创建的目录个数:'))
mkdirs(path, num)
'''
# 上面是老师写的,这里是自己写的,同样能实现相同的功能
import os
import os.path
def create_dir(num):
path = './newdir'
if not os.path.exists(path):
os.mkdir(path)
os.chdir(path)
for i in range(1, num + 1):
os.mkdir(f'{i}')
if __name__ == '__main__':
num = int(input('请输入要创建的目录个数:'))
create_dir(num)
'''
练习三
记录用户登录日志并查看。
需求:
创建XX客服管理系统的登录界面,每次登录时,将用户的登录日志写入文件中,并且可以在程序中查看用户的登录日志。
import time
def show_info():
print('请输入提示数字,执行相应的操作:0.退出 1.查看登录日志')
# 记录日志
def write_loginfo(username):
# 追加写模式
with open('log.txt', 'a', encoding='utf-8') as file:
s = f'用户名:{username},登录时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))}'
file.write(s)
file.write('\n')
# 读取日志的操作
def read_loginfo():
with open('log.txt', 'r', encoding='utf-8') as file:
while True:
# readline()只会读取一行,但是读取完一行之后读写指针会指向下一行,第二次循环时readline()会将第二行读取出来,...,以此类推...
line = file.readline()
if line == '':
break
else:
print(line, end='')
if __name__ == '__main__':
username = input('请输入用户名:')
pwd = input('请输入密码:')
if username == 'admin' and pwd == 'admin':
print('登录成功')
# 将登录信息写入日志文件
write_loginfo(username)
# 提示用户操作
show_info()
num = eval(input('请输入要操作的数字:'))
while True:
if num == 0:
print('退出成功')
break # 退出while
elif num == 1:
print('查看登录日志')
read_loginfo()
show_info() # 再调一次显示信息
else:
print('对不起,您输入的数字有误')
show_info()
num = eval(input('请输入要操作的数字:'))
else:
print('用户名或密码不正确')
练习四
模拟淘宝客服自动回复。
需求:
淘宝客服为了快速回答买家问题,设置了自动回复的功能,当有买家咨询时,客服自助系统会首先使用提前规划好的内容进行回复,请用Python程序实现这一功能。
要回复的内容模版在 reply.txt 文件内:
def find_answer(question):
with open('reply.txt', 'r', encoding='utf-8') as file:
while True:
# readline()只会读取一行,但是读取完一行之后读写指针会指向下一行,第二次循环时readline()会将第二行读取出来,...,以此类推...
line = file.readline() # 第一次 订单|如果您有任何订单问题,可以登录淘宝账号,点击“我的订单”,查看订单详情
if line == '':
break # 退出的是while循环
# 字符串的劈分操作
keyword = line.split('|')[0]
reply = line.split('|')[1]
if keyword in question:
return reply
return False
if __name__ == '__main__':
question = input('HI,XXX你好,小蜜在此等候主人多时了,有什么烦恼和小蜜说说吧:')
while True:
if question == 'bye':
break # 退出循环
else:
# 开始查找要回复的答案
reply = find_answer(question)
if reply == False: # 函数的返回值如果是False
question = input('小蜜不知道你在说什么,您可以问我一些关于订单、物流、支付、账户方面的问题,退出请输入bye:')
else:
print(reply)
question = input('小主,您还可以问一些关于订单、物流、支付、账户方面的问题,退出请输入bye:')
print('小主再见')