文件基本操作¶
对文件的操作有很多种, 常见的操作包括创建, 删除, 修改权限, 读取, 写入等, 这些操作可大致分为两类:
- 删除, 修改权限: 作用于文件本身, 属于系统系操作;
- 写入, 读取: 是文件最常用的操作, 作用于文件的内容, 属于应用级别操作.
对于文件的系统级操作功能单一, 比较容易实现, 可以借助Python中的专用模块(os, sys等), 调用指定的函数来实现.
而对于文件的应用级操作, 通常需要按照固定的步骤进行, 且实现过程相对比较复杂.
文件的应用级操作可以分为以下3步, 每一步都需要借助对应的函数实现:
- 打开文件: 使用
open()函数, 该函数会返回一个文件对象;- 对已打开文件做读/写操作: 读取文件内容可使用
read(),readline()和readlines()函数, 向文件中写入内容, 可以使用write()函数;- 关闭文件: 对文件的读/写操作完成之后, 最后需要关闭文件, 可以使用
close()函数.
一个文件, 必须在打开之后才能对其进行操作, 并且在操作结束之后, 还应该将其关闭.
创建/打开文件¶
如果想要操作文件, 首先需要创建或者打开指定的文件, 并创建一个文件对象, 这些工作可以通过内置的open()函数实现.
open()函数用于创建或打开指定文件, 其常用语法格式如下:
f = open(filename, mode='r', buffering=-1, encoding=None)
file: 创建的文件对象;filename: 要创建或打开的文件的名称;mode: 可选参数, 指定文件的打开模式;buffering: 可选参数, 用于指定对文件进行读写操作时, 是否使用缓冲区;encoding: 指定打开文件时所使用的编码格式, 不同平台的encoding参数值也不同.
注解
系统中默认有三个标准流: sys.stdin(标准输入流), sys.stdout(标准输出流), sys.stderr(标准错误流).
可以读sys.stdin, 可以写sys.stdout和sys.stderr, 就像使用文件对象一样.
文件打开模式¶
文件的访问模式有: r, w, x, a, b, t, +.
- 模式参数是可选的, 默认是
rt(读/文本文件);+可以与其它任何模式组合使用, 表示在原先的打开模式基础之上, 是既可读也可写的.
| 模式 | 描述 |
| r | 只读模式, 若文件不存在, 则引发FileNotFoundError异常 |
| w | 只写模式, 若文件已存在将其覆盖, 若文件不存在则创建新文件 |
| a | 追加模式, 若文件已存在将文件指针放在文件结尾, 若文件不存在则创建新文件 |
| x | 独占模式, 若文件已存在则引发FileExistsError 异常 |
文件格式和二进制格式打开文件的区别¶
实际上, 从计算机的角度来看, 文本文件和二进制文件没有区别, 它们的内容都是以二进制的形式保存在磁盘中的. 对于文本文件, 是将字符按一定的格式编码后(ASCII, UTF-8, GBK等)存储起来, 而对于二进制文件, 是直接将数据按其二进制格式保存. 文本文件通常是可读的, 因为文本文件打开之后, 编辑器根据编码格式, 将其中的二进制数据转换为字符; 二进制文件通常用来保存视频, 图片, 音频等不可读的数据.
使用文件格式和二进制格式打开文件, 唯一的区别是对文件中换行符的处理不同.
在Windows系统中, 文件使用\r\n作为换行符, 当以文本格式读取文件时, 会将\r\n转换成\n;
反之, 以文本格式将数据写入文件时, 会将\n转换成\r\n.
这种隐式转换换行符的行为, 对用文本格式格式打开文件是没有问题的, 但如果用文本格式打开二进制文件, 就有可能改变文件中的数据(将\r\n隐式转换为\n).
而在Unix/Linux系统中, 默认的文件换行符就是\n, 因此在Unix/Linux系统中文本文件和二进制文件并无本质的区别.
总的来说, 为了保险起见, 对于Windows平台最好用b打开二进制文件, 对于Unix/Linux平台, 打开二进制文件, 可以用b, 也可以不同.
是否使用缓冲区¶
通常情况下, 建议在使用open()函数时打开缓冲区, 即不需要修改buffering参数的值.
注解
如果buffering参数的值为0(或者False), 则表示在打开指定文件时不使用缓冲区;
如果buffering参数值为大于1的整数, 该整数用于指定缓冲区的大小(单位是字节);
如果buffering参数的值为负数, 则代表使用默认的缓冲区大小.
为什么呢? 原因很简单, 目前计算机内存的I/O速度仍远远高于外设(例如键盘, 鼠标, 硬盘等)的I/O速度, 如果不使用缓冲区, 则程序在执行I/O操作, 内存和外设就必须进行同步读写操作, 也就是说, 内存必须等待外设输入(输出)一个字节之后,才能再次输入(输出)下一个字节. 这意味着, 内存中的程序大部分时间都处于等待状态.
而如果使用缓冲区, 则程序在执行输出操作时, 会先将所有数据输出至缓冲区, 然后继续执行其它操作, 缓冲区中的数据会有外设自行读取处理; 同样, 当程序执行输入操作时, 会先等外设将数据读入缓冲区中, 无需同外设做同步读写操作.
文件对象的常用属性¶
成功打开文件之后, 可以调用文件对象的属性获取当前文件的部分信息, 其常见属性如下:
file.name: 文件的名称;file.mode: 文件的打开模式;file.encoding: 打开文件时使用的编码格式;file.closed: 判断文件是否已经关闭.
注解
文件最重要的功能就是读写数据.
在文本和二进制模式下, 分别将str和bytes类用作数据.
读取文件¶
读取文件有3个方法可用:
read(): 逐个字符或字节读取文件;readline(): 读取一行;readlines(): 一次性读取文件中的所有行.
read()¶
f.read(size)
size作为一个可选参数, 用于指定一次最多可读取的字符(字节)个数, 如果省略, 则默认一次读取所有内容, 返回读取的字符串或字节对象.
注意, size表示的是一次最多可读取的字符(字节)数, 因此, 即便设置的size大于文件中存储的字符(字节)数, 也不会报错, 它只会读取文件中所有的数据直到文件结束.
注解
如果是以文本模式打开文件, 则read()会逐个字符进行读取;
如果是以二进制模式打开文件, 则read()会逐个字节进行读取.
read()抛出UnicodeDecodeError 异常的解决方法:
在使用
read()时, 如果抛出UnicodeDecodeError异常, 其原因在于: 目标文件使用的编码格式和open()函数打开文件时指定的编码格式不一致.举个例子, 如果目标文件的编码格式为GBK编码, 而我们在使用
open()函数并以文本模式打开该文件时, 手动指定encoding参数为UTF-8. 这种情况下, 由于编码格式不匹配, 当使用read()读取目标文件文件时, Python解释器就会抛出UnicodeDecodeError异常.要解决这个问题, 需要将
open()函数中的encoding参数修改为和目标文件相同的编码格式.除此之外, 还有一种解决方法: 先使用二进制模式读取读取文件, 然后调用
bytes的decode()方法, 使用目标文件的编码格式, 将读取到的字节串解码为字符串.
readline()¶
f.readline(size)
size为可选参数, 用于指定读取一行时, 一次最多读取的字符(字节)数, 如果没有指定, 则默认读取一行直到遇到换行符(\n).
注意, 换行符(\n)也会被读取.
返回读取的字符串或字节对象.
写入文件¶
write()¶
f.write(text)
- 将
text写入到文件中的当前位置(写入的可以是str, 也可以是bytes), 然后返回写入的字节数; - 如果哦
text不是str或bytes, 需要先进行转换; - 需要自己显式添加换行符.
Example:
with open('/tmp/test', 'w') as f:
value = ('hello, world', 11)
s = str(value) # 将非字符串类型转换为字符串
f.write(s)
注意, 在写入文件完成后, 一定要调用close()函数将打开的文件关闭, 否则写入的内容不会保存到文件中.
这是因为, 当我们在写入文件内容时, 操作系统不会立刻把数据写入磁盘, 而是先缓存起来, 只有调用close()函数时, 操作系统才会保证把没有写入的数据全部写入磁盘文件中.
除此之外, 如果向文件中写入数据后, 不想马上关闭文件, 也可以调用文件对象的flush()方法, 它可以实现将缓冲区的数据写入文件中.
writelines()¶
f.writelines(iterable_object)
接收一个字符串列表(实际上可以是任何序列或可迭代对象)作为参数, 并将它们写入到文件(或)流中的当前位置, 需要自己显式添加换行符.
Example:
with open('a.txt', 'w') as f:
l = ['hello', 'world', 'a', 'b', 'c']
f.writelines(l)
关闭文件¶
close()方法用来关闭已经打开的文件:
f.close()
- 处理完一个文件后, 应该总是使用
f.close()来关闭文件并释放系统资源, 尽管这在有时候不是必需的, 但关闭文件没有什么害处; - 如果想确保文件被关闭, 可以使用
try/finally语句, 并在finally子句中调用close()方法, 这样可以确保在任何情况下close()方法都被调用; - 可以使用
with语句打开文件并将文件对象绑定到一个变量, 这样在离开with的范围之后, 会自动关闭打开的文件, 即使是由于异常引起的结束也是如此.
随机存取¶
注解
文件指针用于标明文件读写的位置.
通过移动文件指针, 再借助read()和write()函数, 就可以实现在文件的指定位置读/写.
在Python中, 文件读写共用同一个文件指针.
为实现对文件指针的移动, 文件对象提供了tell()和seek()方法.
tell()方法用于返回文件指针当前所处的位置, seek()方法用于将文件指针移动到指定的位置.
tell()¶
f.tell()
返回当前文件指针的位置, 它是从文件开头计算的字节数.
当使用open()函数打开文件时, 文件指针的起始位置为0, 表示位于文件的开头处.
当使用文件对象读写数据时, 文件指针会自动向后移动: 读写了多少个数据, 文件指针就自动向后移动多少个位置.
seek()¶
f.seek(offset, whence)
将文件指针(读写的起始位置)移动到offset和whence指定的位置.
whence: 可选参数, 用于指定文件指针要放置的位置, 该参数的值有三个选择:io.SEEK_SET(0, 文件的开头, 默认值);io.SEEK_CUR(1, 当前位置);io.SEEK_END(2, 文件的末尾).
offset: 表示相对于whence位置文件指针的偏移量, 正数表示向后偏移, 负数表示向前偏移.
注意
当offset为非0且whence不是0(文件开头)时, Python要求文件必须以二进制格式打开, 否则会抛出io.UnsupportedOperation异常.