Skip to content

[TOC]

before

本小节介绍一个特殊的函数——open函数,因为到目前为止,我们写的程序都非常的简陋,应用范围狭窄,而通过open函数,我们就可以将程序得以扩展到文件和流的领域。

打开文件

通过open函数打开文件,其语法如下:

python
open(
    file, 
    [mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True]
)

open函数打开文件或文件描述符并返回文件对象,如果无法打开则抛出OSError错误。

file参数为字符串或者是字节对象的路径或者相对路径(当前工作目录)。

buffering参数控制着文件的缓冲,如果参数是0或者False,那么就是说无缓冲,所有的读写操作都是直接针对硬盘的操作。如果参数是1或者True,那么意为Python会使用内存代替硬盘,从而让程序变得更快,只有使用flush或者close时,缓冲区的文件才会更新到硬盘。

encoding参数指以什么方式操作文件,该参数只适用于文本模式。

errors参数指定如何处理编码和解码错误。仅适用于文本模式。

newline参数为换行符控制换行符模式的工作方式,仅适用于文本模式,如果该参数为None,那么它的工作方式是,从文件流中读取输入时,启用通用换行符模式,将所有的换行符(\r\n 或者\n)转换为\n,再返回给调用者。

closefd参数为True时,则为传入file参数为文件的文件名。为False时传入的file参数只能是文件描述符。什么是文件描述符?在UNIX平台的系统中,文件描述符就是一个非负数,比如说,打开一个文件,就会得到一个文件描述符。

mode为可选参数,用于指定打开文件的模式,默认为r,以读的方式打开文件,常用的mode模式如下所示:

mode描述
r打开文件,以读的方式,默认的方式
w以写的方式打开文件,如果原文件存在则会被覆盖,如果没有此文件就会创建,前提是该模式下必须保证文件目录的存在
a以追加的方式打开文件,如果文件存在则从原文件的末尾写入,否则会创建文件
rb打开文件,以二进制的形式读文件
ab以二进制追加模式打开文件
wb以二进制写模式打开,打开前原文件会被清空

一般地,如果Python处理的是文本文件,使用rw模式没有任何问题。但有时会处理一些其他类型文件(二进制文件),如音、视频文件,那么就应该在模式参数中增加b模式,如rb模式读取二进制文件。那么为什么使用rb模式?

如果使用rb模式,通常跟r模式不会有太大区别,仍然是读取一定的字节,并且能执行文本文件的相关操作。Python使用二进制模式关键点是给出原样的文件。而文本模式下则不一定。

因为在于Python对于文本文件的操作方式有些不同,其中就有标准化换行符。一般的,Python的标准换行符是\n,表示结束一行并另起一行,这也是UNIX系统的规范。而Windows系统中则是\r\n,但无需担心,Python会自动在各平台间(包括Mac平台)帮我们转换。但这并不足以解决问题,因为二进制文件中(音、视频)很可能包含这些换行符。如果Python以文本模式处理这些文件,那么很可能就破坏了文件。为了避免此类问题,就要以二进制的方式操作这些文件,这样在操作中就不会发生转换从而避免文件损坏。

如果在读的时候以通用的模式读取文件,则所有的文件都统一转换为\n,从而不用考虑平台问题。

open函数常用的参数为file、encoding、mode三个参数。

常用方法

既然打开了文件,就要对文件做些什么了!接下来介绍文件对象的一些方法。

对文件的操作最重要的就是读和写了,那么拿到一个文件对象f时,通过f.readf.write两个方法完成读写操作。

w模式和write

python
f1 = open('t1.txt', mode='w')
f1.write('hello')
f1.write('world')
f1.close()

上例,open函数以写的方式打开t1.txt文件并将文件句柄f1返回,w模式的特点是如果文件不存在就创建文件,文件存在就覆盖写,并且这个文件句柄只能写入普通的文本字符串(写入bytes类型的文件会报错)。通过文件句柄调用写方法将两个字符串写入文件中,然后关闭文件句柄。此时如果你打开t1.txt文件的话,会发现这两个字符串紧挨着在一行上。这是因为两个字符串之间没有换行符\n

来,我们加上换行符:

python
f1 = open('t1.txt', mode='w')
f1.write('hello\n')
f1.write('world\n')
f1.close()

现在,这两个字符串各占一行了。

r模式和read系列

那么如何读文件呢?

python
f2 = open('t1.txt', mode='r')
content = f2.read()
print(content)
f2.close()
"""
hello
world
"""

上例,首先以读模式拿到文件句柄f2,然后使用read方法一次性读取t1.txt内的所有内容。注意,r模式读取普通的文本字符串(读取bytes类型的文本文件会报错)。最后关闭文件句柄。

这里思考一个问题,read是一次性的读取所有文件内容,但如果文件特别大的话,比如有两个G的日志文件,将这么大的文件一次性的读取到内存中,是不合理的,所以,可以在read的时候指定每次读的字节数大小:

python
f3 = open('t1.txt', mode='r')
content = f3.read(5)
print(content)
content = f3.read(5)
print(content)
f3.close()
"""
hello

worl
"""

上例,当第一次读的时候,读了5个字节的内容hello,第二次读的时候,从第一次读的位置后面继续往后读5个字节,首先读到了一个换行符\n,然后在读第二行的四个字节worl

除了在read,还有其他的读方法:

python
f4 = open('t1.txt', mode='r')
content = f4.readline()
print(content)
f4.close()

f5 = open('t1.txt', mode='r')
content = f5.readlines()
print(content)
f5.close()
"""
hello

['hello\n', 'world\n']
"""

上例,readline每次读一行(两次打印结果之间的空行是第一行的\n),readlines读取所有行,并以列表的形式返回。

a模式

有些情况不能覆盖写,而是要追加写,比如日志文件,这就用到了a模式——追加写:

python
f6 = open('t1.txt', 'a')
for i in "hello":
    f6.write('{}{}'.format(i, '\n'))
f6.close()

追加写模式中,如果文件存在,则以追加的方式继续写入,如果文件不存在就创建文件,然后以追加的方式写入。

wb/ab/rb

之前处理的都是文本字符串,要处理音视频、图片等二进制类型的文件时,就需要搭配使用b模式了,b模式标识处理的是二进制文件:

python
f7 = open('t1.png', 'rb')
content = f7.read()

f8 = open('t2.png', 'wb')
f8.write(content)

f7.close()
f8.close()

上例,要处理的是图片类型的文件,所以要使用b模式来读写。如果要传输的是视频之类的,就要考虑使用ab模式了。

seek/tell

除了上面read系列的顺序读取文件,Python还提供了随机读取文件方法:

python
f9 = open('t1.txt', 'r')
print(f9.tell())
f9.seek(2)
print(f9.tell())
content = f9.read()
print(content)
print(f9.tell())
f9.close()
"""
0
2
llo
world
12
"""

上例,seek(offset, [whence])方法意为把当前读(或写)的位置移动到由offsetwhence定义的位置,offset表示偏移的字节量,而whence为可选参数,搭配offset使用,表示从哪个位置开始偏移,0表示从文件开头开始,1代表从当前位置开始开始,2表示从文件末尾开始偏移。 而tell方法则返回当前指针所在的位置。

关闭文件

我们为什么在对文件对象f操作完毕之后,都要去关闭它?

python
f14 = open('t1.txt', 'w')
f14.write('hello\n')
# f14.close()
f15 = open('t1.txt', 'r')
content = f15.read()
print(content)

上例,如果f14文件句柄不关闭,f15文件句柄则无法读取文件内容。这只是其中一种情况。

有时候,Python解释器因为某些原因如提高程序运行速度,会将文件缓存在内存中某个地方。但碰到意外如程序突然崩溃,就会造成这些缓存数据没有及时写入硬盘。也为了降低系统对打开文件的资源占用,如在Linux系统中,对打开文件数会有限制,超过限制则无法打开文件。为了避免这些可能出现的问题,在对文件处理完毕之后及时关闭文件。

而关闭文件则有两种方式,一起来看看吧!

手动关闭文件

手动关闭就是使用f.close来完成。这无需多说了:

python
f14 = open('t1.txt', 'w')
f14.write('hello\n')
f14.close()
f15 = open('t1.txt', 'r')
content = f15.read()
print(content)
f6.close()

自动关闭文件

但每次都手动关闭文件,比较麻烦,Python又提供了with语句来解决这个麻烦。

with语句:

python
with open(...) as f:
    f.read()

open函数的参数不变,关键字as后面的文件对象f可以自定义。

with语句允许使用上下文管理器,上下文管理器则是支持__enter____exit__方法的对象。__enter__方法没有参数,当程序执行到with语句的时候被触发,返回值绑定在文件对象f上。而__exit__方法则有三个参数,包括异常类型、异常对象、异常回溯。现在无需深入了解这个三个参数,只需知道当with语句执行完毕,也就是对文件的操作执行完毕,with会自动执行__exit__方法来关闭文件。

来个示例:

python
with open('t1.txt', 'w') as f:
    f.write('with 语句真省事')
    
with open('t1.txt', 'r') as f:
    f.read()  # with 语句真省事

with语句无疑帮我们做了很大的工作,让我们专心于文件操作本身。

当然了,with语句的功能不仅限于此,它还有其他的用法,这里不做过多的探讨。

f是什么?

我们对一个文件操作,总是要拿到这个文件句柄来做操作,那么这个文件句柄是什么呢?

python
from collections import Iterable

f = open('t1.txt', 'r')
print(isinstance(f, Iterable))  # True

由上例可以看到,文件句柄f它也是一个可迭代对象,那么我们就可以通过循环来取值。

python
f = open('t1.txt', 'r')
for i in f:
    print(i)

"""
hello

world
"""

每次循环读取一行数据。