在前面我们学习了变量、序列、对象,在他们中存储的数据是暂时的,程序结束数据就会丢失。为了能够长时间保存数据,需要将数据保存到磁盘文件。
本篇博客就来介绍如何对磁盘数据进行读写等操作。
1. 数据读取理论
在python中要想读取数据,需要先将磁盘文件打开,建立数据传输的通道。
1.1 打开文件
打开文件使用open函数,open函数基本语法格式如下:
file =open(filenam,mode = 'r',buffering = -1, encoding = NOne,errors = None, closefd = True, opener = NOne)
open函数用于打开文件并返回流,当打开失败时会出现OSError错误。
主要用到的参数:
1)filename: 文件名称
(1) 文件可以是文本文件或二进制文件
(2)文件名称需要使用单引号或者双引号括起来
(3)引号内部有3种情况:
a. 要打开的文件和当前文件在同一目录下: 可以使用相对路径。
b. 要打开的文件与当前文件不在同一目录下,必须使用完整的绝对路径
c. 还可以使用文件描述符。(我还不清楚)
2)mode: 文件的打开模式
取值 | 说明 | 指针位置 |
r | 以只读方式打开文件 | 文件开头 |
r+ | 打开文件后,可以读取文件内容,也可以写入新的内容覆盖原有内容 | 文件开头 |
rb+ | 以二进制方式打开文件,且采用读取的方式 | 文件开头 |
w | 以只写方式打开文件 | 文件开头 |
w+ | 打开文件后,先清空原有内容,对空文件进行写入 | 文件开头 |
wb+ | 以二进制方式打开文件,且采用写入的方式 | 文件开头 |
a | 以追加方式打开文件, | 文件结尾 |
w+ | 打开文件后,先清空原有内容,对空文件进行写入 | 文件结尾 |
wb+ | 以二进制方式打开文件,且采用写入的方式 | 文件结尾 |
1.2 读取文件
1.2.1 调用read方法读取
read函数从open函数返回的流中解码数据并返回结果
file.read(size = -1)
参数含义:
1) size: 可选参数,指定要读取的字符个数,read采取的是贪婪读取模式,默认值表示读取全部。size的设定可以在面对大文件时,
避免一次性读取过多内容,可以按照需求设置读取长度。
注意:read方法读取文件时,是从文件的开头开始读取的,读取完毕指针移到文件末尾。
1.3 写入文件
python对象提供了write()方法,可以向文件中写入内容,语法格式如下:
file.write(string)
1.4 关闭文件
打开文件后,需要及时关闭,忘记关闭文件会造成系统资源消耗,且会影响后续文件的访问。文件关闭使用文件对象的close方法实现。语法格式如下:
file.close()
2. 演练
2.1 读取文件
2.1.1 mode = 'r’与mode='r+'模式
使用非二进制的mode = 'r’与mode='r+'模式读取文件
原文件是这样的
接下来,先演练读取数据,读取数据后要及时调用file.close关闭
以mode = 'r' 或者mode = 'r+'的打开文件结果如下:
以mode = 'r' 或者mode = 'r+'的方式读取文件:
mode = 'r+' 模式,不仅可以使用read方法读取文件,而且也可以使用write方法写入新的内容。而mode = 'r' 模式为只读模式,不能使用write方法
可以看到,mode ='r+'模式,不仅可以读取数据,而且支持写入数据。但是要注意,当打开文件之后,第一次调用read()方法读取文件后,
指针在从开头移动到了文件末尾,这时再执行write()方法会在此时指针的位置往后继续写入,而写入之后文件指针在添加了写入内容的
整个文件的末尾,因此再调用read()函数时,并不能读取到文件内容。
当先写入后读取文件,来看下运行情况:
这个文件的运行如下:首先打开文件,指针此时在文件开头,然后调用write()方法,会从指针的位置向后写入write()中的内容:
已存在文件的r+模式打开;此时指针移动到“开”字后面,而原文件中的“相对人类的欲望而言,资”这几个字符被替换为长度相等的
“已存在文件的r+模式打开”(write()方法中应该1个汉字占据两个字符),因此下面调用read()方法时,指针从“开”字后面的“源”开始,
输出:源是稀缺的!!!
总结:
1)以text mode 形式打开的文件返回TextIOWrapper。
2)open()函数建立了磁盘数据传输的通道,返回流对象。
3)open()函数执行后,文件指针位于文件开头
4)read()函数以贪婪模式执行,默认读取全部文件数据
5)read()函数执行后,文件指针位于文件末尾
2.1.2 以二进制方式打开文件
mode = 'rb’或mode=‘rb+’
同理mode = 'rb’模式是只读模式,无法进行write()方法的写入操作
mode = 'rb+'模式,不仅可以读取文件,而且可以写入文件,但是写入内容必须为字节对象。这里使用
bytes函数转换。
2.2 以写模式打开文件
2.2.1 以只写模式打开文件
以只写模式mode = 'w’或者mode = 'wb’模式打开文件,只能进行write()方法的操作,不能进行read()方法操作。
以只写模式,若原文件存在,会先清空原文件内容,再进行写入。
2.2.2 以读写方式打开文件
总结:
1) mode = 'w' 或mode = 'wb'模式为只写模式,不能使用read()方法
2)mode = 'w+'或mode='wb+'模式为读写模式,可以使用read()方法;但是可以看到read()的内容为空,
这是因为'w'的相关模式在打开文件后,会先清空原有内容,使其转变为空的文件,因此,
第一次的read()方法输出为空,写入内容后,文件指针在文件末尾,因此,第二次的read()方法输出为空。
2.3 以追加模式打开文件
2.3.1 以追加模式打开文件
可以看到以mode = 'a’或者mode = 'ab’追加模式打开文件是不能读取文件内容的。
ecomi = open('C:\\Users\\changyanhua\\Desktop\\testw.txt', mode='a')
print(ecomi)
# print(ecomi.read())
# print('*'*20)
ecomi.write('花好月圆')
# ecomi.write(bytes('基本文件操作', 'utf-8'))
# print(ecomi.read())
ecomi.close()
# 相对人类的欲望而言,资源是稀缺的!!!
ecomi = open('C:\\Users\\changyanhua\\Desktop\\testw.txt', mode='ab')
print(ecomi)
# print(ecomi.read())
# print('*'*20)
# ecomi.write('花好月圆')
ecomi.write(('二进制'.encode()))
# print(ecomi.read())
ecomi.close()
总结:
1)以追加模式打开的文件不能进行读取操作
2)文件打开后,文件指针放在文件末尾,新内容写入已有内容之后。
2.3.2 以读写模式打开
![在这里插入图片描述](https://img-blog.csdnimg.cn/c28c8b81fad44caa899cf498faa8f49e.png
总结:
1)采用mode = 'a'或者mode='ab'无法调用read()方法
2)采用mode = 'a+'或者mode='ab+'可以调用read()方法,但是同前面一样的道理,文件指针在文件末尾,所以没有读取到数据。
下面来看以二进制方式打开文件,结果根据mode模式不同而不同:
以mode = 'rb',只读二进制格式打开,返回BufferedReader
以mode = 'wb',采用只写;或者mode = 'ab'追加模式二进制格式打开,返回BufferedWriter
以mode = 'rb+',或者mode = 'wb+'或者mode = 'ab+'读写模式二进制格式打开,返回BufferedRandom
3. 读取文件
3.1 read()方法
read函数从open函数返回的流中解码数据并返回结果
file.read(size = -1)
参数含义:
1) size: 可选参数,指定要读取的字符个数,read采取的是贪婪读取模式,默认值表示读取全部。size的设定可以在面对大文件时,
避免一次性读取过多内容,可以按照需求设置读取长度。
注意:read方法读取文件时,是从文件的开头开始读取的,读取完毕指针移到文件末尾。
1)结合前面代码可以看到,read()函数默认读取全部字符
2)还可以通过size指定读取字符的个数:1个汉字算一个字符。
3.2 read.line()方法
当文件很大,一次性读取全部内容,容易造成内存不足,可以逐行读取,采用read.line()方法
还可以使用readline逐行读取全部内容,判断依据以读取内容是否为空来判断:
# 相对人类的欲望而言,资源是稀缺的!!!
ecomi = open('C:\\Users\\changyanhua\\Desktop\\ec_test.txt', mode='r')
print(ecomi)
i = 0
while True:
line_con = ecomi.readline()
print(line_con)
if line_con == '\n':
i += 1
print(i)
if line_con == '':
break
ecomi.close()
1)中间中间的空白部分读取内容是换行符'\n',并不是空,所以有空白也不影响读取至文件结束。
2)输出代码中line_con的类型,显示为str;因此readline的返回结果为字符串。
3.3 readlines()方法
从代码运行结果可以看出:
1) readlines读取全部文件内容;
2)readlines的返回结果为字符串列表,列表的每个元素记录一行内容;
3)返回内容不仅包括文本内容,还包括每行的换行符: \n;
对于结果列表,使用for循环遍历,可以得到与read()方法一样的结果
ecomi = open('C:\\Users\\changyanhua\\Desktop\\ec_test.txt', mode='r')
print(ecomi)
lin_cons = ecomi.readlines()
for item in lin_cons:
print(item)
4. 文件指针
4.1 移动文件指针:seek()
前面在读写函数时,经常发生文件指针在文件结尾,导致读取内容为空的情况,如果想要得到内容或者想要读取部分内容,可以移动文件指针的位置,使用seek()方法
file.seek(offset[,whence])
参数含义:
1) offset: 移动的字符个数,seek()方法中,汉字占两个字符
2)whence: 指定从什么位置开始计算。
(1)0: 从文件头开始
(2)1:从当前位置开始
(3)2: 从文件末尾开始
不指定移动字符的个数,默认移动整个文件字符的长度:
# 相对人类的欲望而言,资源是稀缺的!!!
ecomi = open('C:\\Users\\changyanhua\\Desktop\\testw.txt', mode='r+')
print(ecomi)
cons = ecomi.read()
print(cons)
print('*'*20)
ecomi.write('\n移动文件指针')
# 未人为移动指针时:
pre_cons = ecomi.read()
print(pre_cons)
print('*'*20)
ecomi.seek(0)
# 主动移动指针后:
aft_cons = ecomi.read()
print(aft_cons)
指定移动字符的长度(汉字及其符号,占两个字符长度)
# 相对人类的欲望而言,资源是稀缺的!!!
ecomi = open('C:\\Users\\changyanhua\\Desktop\\testw.txt', mode='r+')
print(ecomi)
cons = ecomi.read()
print(cons)
count = 2* len(cons)
print(count)
print('*'*20)
ecomi.write('\n移动文件指针')
# 未人为移动指针时:
pre_cons = ecomi.read()
print(pre_cons)
print('*'*20)
ecomi.seek(count, 0)
# 主动移动指针后:
aft_cons = ecomi.read()
print(aft_cons)
如果文件不是使用二进制方式打开是只能从文件开头开始计算,即:whence取值只能为0,否则会出现异常:
4.2 返回文件指针位置:tell()
关键点:
1)打开文件后文件指针位置在开头0
2)一个汉字占两个字符
3)每行结尾的换行符'\n'占两个字符