文件读写
在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符或文件句柄),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
open() 方法
Python open() 打开一个文件,并返回文件对象,如果该文件无法被打开,会抛出 OSError。
注意:使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。
open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。
open(file, mode=‘r’)
完整的语法格式为:
open(file, mode=‘r’, encoding=None, errors=None, buffering=-1, newline=None, closefd=True, opener=None)
参数说明:
• file: 必需,文件路径(相对或者绝对路径)。
• mode: 可选,文件打开模式
• buffering: 设置缓冲
• encoding: 一般使用utf8
• errors: 报错级别
• newline: 区分换行符
• closefd: 传入的file参数类型
• opener:
mode 参数有:
模式 描述
x 写模式,新建一个文件,如果该文件已存在则会报错。
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。只写,存在则覆盖,不存在则新建。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

  • 打开一个文件进行更新(可读可写)。
    r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
    w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    b 二进制模式。
    rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
    wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
    wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
    默认为文本模式,如果要以二进制模式打开,加上 b 。
    下图很好的总结了这几种模式:
    模式 r w a w+ r+ a+
    读 + + + +
    写 + + + + +
    创建 + + + +
    覆盖 + +
    指针在开始 + + + +
    指针在结尾 + +
    file 对象
    file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:
    序号 方法 描述
    1 file.read([size])
    从文件读取指定的字节数,如果未给定或为负则读取所有。
    2 file.readline([size])
    读取整行,包括 “\n” 字符。如果指定了一个非负数的参数,则返回指定大小的字节数,包括 “\n” 字符。
    3 file.readlines([sizeint])
    读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
    4 file.write(str)
    将字符串写入文件,返回的是写入的字符长度。
    5 file.writelines(sequence)
    向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。
    6 file.flush()
    刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
    7 file.tell()
    返回文件当前位置。
    8 file.seek(offset[, whence])
    移动文件读取指针到指定位置
    9 file.close()
    关闭文件。关闭后文件不能再进行读写操作。

11 file.truncate([size])
从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
12 file.fileno()
返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
13 file.isatty()
如果文件连接到一个终端设备返回 True,否则返回 False。
文件对象的方法
每当我们用open方法打开一个文件时,将返回一个文件对象。这个对象内置了很多操作方法。下面假设,已经打开了一个f文件对象。
f.read()
为了读取一个文件的内容,调用 f.read([size]), 这将读取一定数目的数据, 然后作为字符串或字节对象返回。当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。
以下实例假定文件 foo.txt 已存在(上面实例中已创建):
实例

打开一个文件

f = open("/tmp/foo.txt", “r”)

str = f.read()
print(str)

关闭打开的文件

f.close()
f.readline()
f.readline() 会从文件中读取单独的一行。换行符为 ‘\n’。f.readline() 如果返回一个空字符串, 说明已经已经读取到最后一行。
实例

打开一个文件

f = open("/tmp/foo.txt", “r”)

str = f.readline()
print(str)

关闭打开的文件

f.close()
f.readlines()
f.readlines() 将返回该文件中包含的所有行。
如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。
实例

打开一个文件

f = open("/tmp/foo.txt", “r”)

str = f.readlines()
print(str)

关闭打开的文件

f.close()
遍历文件,另一种方式是迭代一个文件对象然后读取每行:
实例

打开一个文件

f = open("/tmp/foo.txt", “r”)

for line in f:
print(line, end=’’)

关闭打开的文件

f.close()
这个方法很简单,不需要将文件一次性读出,但是同样没有提供一个很好的控制,与readline方法一样只能前进,不能回退。
几种不同的读取和遍历文件的方法比较:如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便。普通情况,使用for循环更好,速度更快。
f.write()
将字符串或bytes类型的数据写入文件内, 然后返回写入的字符数。write()动作可以多次重复进行,其实都是在内存中的操作,并不会立刻写回硬盘,直到执行close()方法后,才会将所有的写入操作反映到硬盘上。在这过程中,如果想将内存中的修改,立刻保存到硬盘上,可以使用f.flush()方法,但这可能造成数据的不一致。
实例

打开一个文件

f = open("/tmp/foo.txt", “w”)

num = f.write( “Python 是一个非常好的语言。\n是的,的确非常好!!\n” )
print(num)

关闭打开的文件

f.close()
实例

打开一个文件

f = open("/tmp/foo1.txt", “w”)

value = (‘www.runoob.com’, 14)
s = str(value)
f.write(s)

关闭打开的文件

f.close()
f.tell()
f.tell() 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。一定要注意了,是字节数,不是字符数。
f.seek()
如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数。
from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,例如:
• seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
• seek(x,1) : 表示从当前位置往后移动x个字符
• seek(-x,2):表示从文件的结尾往前移动x个字符
from_what 值为默认为0,即文件开头。下面给出一个完整的例子:

f = open(’/tmp/foo.txt’, ‘rb+’)
f.write(b’0123456789abcdef’)
16

f.seek(5) # 移动到文件的第六个字节
5

f.read(1)
b’5’

f.seek(-3, 2) # 移动到文件的倒数第三字节
13

f.read(1)
b’d’
f.close()
关闭文件对象。当处理完一个文件后,调用f.close()来关闭文件并释放系统的资源。文件关闭后,如果尝试再次调用该文件对象,则会抛出异常。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了,或者更糟糕的结果。

f.close()
f.read()
ValueError: I/O operation on closed file
二进制文件
前面讲的默认都是读取文本文件,并且是UTF-8编码的文本文件。
二进制模式,通常用来读取图片、视频等二进制文件。注意,它在读写的时候是以bytes类型读写的,因此获得的是一个bytes对象而不是字符串。在这个读写过程中,需要自己指定编码格式。在使用带b的模式时一定要注意传入的数据类型,确保为bytes类型。
s = ‘this is a test’
b = bytes(s,encoding=‘utf-8’)

f = open(‘test.txt’,‘w’)
f.write(s)

这样没问题,正常写入了文件。

s = ‘this is a test’
b = bytes(s,encoding=‘utf-8’)

f = open(‘test.txt’,‘wb’) # 注意多了个b
f.write(s)

报错

TypeError: a bytes-like object is required, not ‘str’

意思是它需要一个bytes类型数据,你却给了个字符串

s = ‘this is a test’
b = bytes(s,encoding=‘utf-8’)

f = open(‘test.txt’,‘wb’) # 注意多了个b
f.write(b) # 将变量b传给它,b是个bytes类型

  • 模式:
    对于w+模式,在读写之前都会清空文件的内容,建议不要使用!
    对于a+模式,永远只能在文件的末尾写入,有局限性,建议不要使用!
    对于r+模式,也就是读写模式,配合seek()和tell()方法,可以实现更多操作。
    字符编码
    要读取非UTF-8编码的文件,需要给open()函数传入encoding参数,例如,读取GBK编码的文件:

f = open(‘gbk.txt’, ‘r’, encoding=‘gbk’)
f.read()
‘GBK’
遇到有些编码不规范的文件,可能会抛出UnicodeDecodeError异常,这表示在文件中可能夹杂了一些非法编码的字符。遇到这种情况,可以提供errors参数,表示如果遇到编码错误后如何处理。

f = open(‘gbk.txt’, ‘r’, encoding=‘gbk’, errors=‘ignore’)
with关键字
为了防止诸如open这一类文件打开方法在操作过程出现异常或错误,一旦出错,后面的f.close()就不会调用,或者最后忘了执行close方法,文件非正常关闭等可能导致文件泄露、破坏的问题。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try … finally来实现:
try:
f = open(’/path/to/file’, ‘r’)
print(f.read())
finally:
if f:
f.close()
但是每次都这么写实在太繁琐,Python提供了with这个上下文管理器机制,保证文件会被正常关闭。在它的管理下,不需要再写close语句。注意缩进。
with open(‘test.txt’, ‘w’) as f:
f.write(‘Hello, world!’)
with支持同时打开多个文件:
with open(‘log1’) as obj1, open(‘log2’,‘w’) as obj2:
s=obj1.read()
obj2.write(s)