文件处理
1 文件概述
1.1 什么是文件
文件是操作系统提供给使用者(用户/应用程序)用于操作硬盘的一种虚拟功能/接口。
1.2 为什么需要文件
用户/应用程序可以通过文件将数据永久保存的硬盘中。
用户/应用程序直接操作的对象是文件,对文件进行的所有的操作,都是在向操作系统发送系统调用,然后再由操作系统将这些操作转换成具体的硬盘操作。
2 如何使用文件
文件处理主要使用函数open()
open() 函数用于打开一个文件,并返回文件对象,也称为文件句柄。
open() 函数一般接收两个参数:文件名(file)和模式(mode)
2.1 文件打开模式分类
读写内容和读写操作
2.1.1 控制读写内容的模式: t 和 b
t 和 b 不能单独使用,必须跟 r/w/a 连用
t - 文本(默认)
读写是以str (unicode) 为单位进行的,
只针对文本文件,图片视频等都不适用,
需要指定编码格式参数encoding。
b - bytes (二进制数字)
2.1.2 控制读写操作的模式 r,w,a
r — 只读
w — 只写
a — 只追加写
-
- 在模式原有功能的基础上追加其不具备的功能
r+ — 读 + 写
w+ — 写 + 读
a+ — 只追加写 + 读
- 在模式原有功能的基础上追加其不具备的功能
2.2 操作文件的基本流程
- 打开文件
- 具体操作:读/写
- 关闭文件
2.2.1 打开文件
open(file, mode=‘rt’, encoding=None)
file: 文件路径
mode: 文件打开模式,默认为 rt
encoding: 文件编码,默认为 None
返回文件对象
2.2.2 文件路径
- 输入路径时避免转义
open(r'C:\a\b\c.txt') # rawstring
open('C:/a/b/c.txt') # 使用 /
- 绝对路径:相对于根目录的路径
相对路径:相对于当前文件的路径
2.2.3 文件编码问题
fileObject.read()
将硬盘中的数据读入内存中。
硬盘上存储的都是二进制数据,read()是将二进制数据读取到内存中
当读取的文件是文本文件时,不能以二进制的格式直接展示给用户。在打开文件时指定参数mode,即以t模式(文本模式)打开文件时,会进行转换。
内存中的字符是以unicode格式存储的。假设读取的文本在硬盘上是以utf-8格式存储的二进制数据,这个转换过程就是将utf-8格式的二进制数据转换为unicode格式的数据。此时就需要指定open()的另一个参数encoding=‘utf-8’。
当以t模式打开文件时,需要指定encoding参数,为文本文件保存时的编码格式,否则可能会出现乱码的问题。
2.2.4 回收资源
打开文件所占用的资源包括两部分:应用程序和操作系统
f = open(r'C:\a\b\c.txt')
- 将文件对象交给变量f,占用应用程序的内存空间;
- 向操作系统发送请求,要求打开文件,操作系统会在自己的内存空间中分配一部分来维护打开的文件,因此也占用操作系统的内存空间;
- 文件对象会对应操作系统打开的文件,通过操作系统映射到硬盘空间上,即应用程序发送系统调用,由操作系统控制硬盘,将内容从硬盘加载到内存中,或者从内存中写入到硬盘上。
回收资源
- 回收应用程序的资源::del f
解除变量名f与内存空间的绑定关系,之后被解释器的垃圾回收机制回收。 - 回收操作系统的资源:f.close()
操作系统同时打开的文件数有上限,因此关闭文件来回收操作系统的资源就十分重要。
2.2.4 上下文管理 with
with open('filepath') as f:
pass
# 同时打开多个文件 一个with
with open('filepath1') as f1,\
open('filepath2') as f2:
pass
2.3 模式详解
t - 文本(默认)
- 读写是以str(unicode)为单位进行的,
- 只针对文本文件,
- 需要指定编码格式参数encoding。
with open('filepath', mode='rt') as f:
res = f.read()
f.write('test')
读的过程:
硬盘上存储utf-8格式的二进制数据
将数据从硬盘加载到内存中,t 模式规定读写过程必须是以str(unicode)为单位进行,
因此,t 模式会将 f.read() 读出的结果解码成unicode。
如果不指定编码格式,操作系统不知道以什么编码格式去解码,就以操作系统默认的编码格式解码,可能会出现乱码。
linux/mac: utf-8
windows: gbk
写的过程:
将数据从内存写入到硬盘中,数据在内存中以unicode格式存储,因此需要按照特定的格式编码成二进制数据才能写入硬盘中。
例如encoding=‘utf-8’,将内存中的unicode格式的字符转换为utf-8格式的二进制数据写入硬盘中。
t 模式自动按照指定的编码格式完成编码/解码过程,不需要调用encode()和decode()方法。
r - 只读模式(默认)
with open('filepath', mode='rt', encoding='utf-8') as f:
res = f.read()
f.write('test') # r模式下报错,不可写
文件不存在时会报错
文件存在时文件指针位于文件起始位置
f.read() 会从文件指针所处的位置读取所有内容,直到文件结尾。
with open('filepath', mode='rt', encoding='utf-8') as f:
res = f.read()
# 重新打开文件,指针位于文件起始位置
with open('filepath', mode='rt', encoding='utf-8') as f:
res = f.read()
按行读取文件
with open('filepath', mode='rt', encoding='utf-8') as f:
for each_line in f:
print(each_line.strip()) # 去掉'\n'
w - 只写模式
文件不存在时会创建空文件
文件存在时会清空文件,文件指针位于起始位置
with open('filepath', mode='wt', encoding='utf-8') as f:
res = f.read() # w模式下报错,不可读
f.write('test')
f.write('test')
以w模式打开文件,在没有关闭时可以连续写,新内容会在已存在内容之后继续写入。
a - 只追加写模式
with open('filepath', mode='at', encoding='utf-8') as f:
res = f.read() # a模式下报错,不可读
f.write('test')
f.write('test')
文件不存在时会创建空文件
文件存在时文件指针位于末尾位置
w 模式与 a 模式的异同:
相同点:在打开的文件不关闭的情况下,两种模式都可以实现连续写入,新写入的内容会跟在已存在内容之后。
不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写的内容永远写在已存在内容之后。
案例:文本文件拷贝
with open(r'src_filepath', mode='rt', encoding='utf-8') as f1, \
open(r'dst_filepath', mode='wt', encoding='utf-8') as f2:
res = f1.read()
f2.write(res)
+ 模式
可读可写
- 模式不能单独使用,需要配合 r,w,a
- 模式取决于原模式,例如 r+ 的限制取决于r,
r+:文件不存在时报错;
w+:文件不存在时创建新文件,文件存在时在open()阶段清空文件,即w+模式无法用于读取源文件内容;
a+:文件不存在时创建新文件,文件存在时文件指针直接移动到文件末尾。对应a/a+模式,写操作都是从末尾追加,此时指针移动无效。
3 练习
3.1 编写文件copy工具
src_path = input('请输入源文件路径').strip()
dst_path = input('请输入目标文件理解:').strip()
with open(r'{}'.format(src_path), mode='rt', encoding='utf-8') as src_file,\
open(r'{}'.format(dst_path), mode='wt', encoding='utf-8') as dst_file:
res = src_file.read()
dst_file.write(src_file)
3.2 编写登录程序,账号密码来自于文件
user_dict = {}
with open('./user_info.txt', mode='rt', encoding='utf-8') as user_file:
line_list = user_file.read().split()
for each_line in line_list:
temp_list = each_line.strip().split(':') # 需要去掉\n
user_dict[temp_list[0]] = temp_list[1]
un_input = input('请输入用户名:').strip()
pw_input = input('请输入密码:').strip()
if un_input not in user_dict:
print('用户名不存在。')
elif user_dict[un_input] != pw_input:
print('输入密码错误。')
else:
print('登录成功。')
3.3 编写注册程序,账号密码存入文件
un_input = input('请输入用户名:').strip()
pw_input = input('请输入密码:').strip()
with open('./user_info.txt', mode='at', encoding='utf-8') as user_file:
user_file.write(f'{un_input}:{pw_input}\n')
print('完成注册。')