[TOC]
before
本小节介绍一个特殊的函数——open函数,因为到目前为止,我们写的程序都非常的简陋,应用范围狭窄,而通过open函数,我们就可以将程序得以扩展到文件和流的领域。
打开文件
通过open函数打开文件,其语法如下:
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处理的是文本文件,使用r
、w
模式没有任何问题。但有时会处理一些其他类型文件(二进制文件),如音、视频文件,那么就应该在模式参数中增加b
模式,如rb
模式读取二进制文件。那么为什么使用rb
模式?
如果使用rb
模式,通常跟r
模式不会有太大区别,仍然是读取一定的字节,并且能执行文本文件的相关操作。Python使用二进制模式关键点是给出原样的文件。而文本模式下则不一定。
因为在于Python对于文本文件的操作方式有些不同,其中就有标准化换行符。一般的,Python的标准换行符是\n
,表示结束一行并另起一行,这也是UNIX系统的规范。而Windows系统中则是\r\n
,但无需担心,Python会自动在各平台间(包括Mac平台)帮我们转换。但这并不足以解决问题,因为二进制文件中(音、视频)很可能包含这些换行符。如果Python以文本模式处理这些文件,那么很可能就破坏了文件。为了避免此类问题,就要以二进制的方式操作这些文件,这样在操作中就不会发生转换从而避免文件损坏。
如果在读的时候以通用的模式读取文件,则所有的文件都统一转换为\n
,从而不用考虑平台问题。
open函数常用的参数为file、encoding、mode三个参数。
常用方法
既然打开了文件,就要对文件做些什么了!接下来介绍文件对象的一些方法。
对文件的操作最重要的就是读和写了,那么拿到一个文件对象f
时,通过f.read
和f.write
两个方法完成读写操作。
w模式和write
f1 = open('t1.txt', mode='w')
f1.write('hello')
f1.write('world')
f1.close()
上例,open函数以写的方式打开t1.txt
文件并将文件句柄f1
返回,w
模式的特点是如果文件不存在就创建文件,文件存在就覆盖写,并且这个文件句柄只能写入普通的文本字符串(写入bytes类型的文件会报错)。通过文件句柄调用写方法将两个字符串写入文件中,然后关闭文件句柄。此时如果你打开t1.txt
文件的话,会发现这两个字符串紧挨着在一行上。这是因为两个字符串之间没有换行符\n
。
来,我们加上换行符:
f1 = open('t1.txt', mode='w')
f1.write('hello\n')
f1.write('world\n')
f1.close()
现在,这两个字符串各占一行了。
r模式和read系列
那么如何读文件呢?
f2 = open('t1.txt', mode='r')
content = f2.read()
print(content)
f2.close()
"""
hello
world
"""
上例,首先以读模式拿到文件句柄f2
,然后使用read方法一次性读取t1.txt
内的所有内容。注意,r
模式读取普通的文本字符串(读取bytes类型的文本文件会报错)。最后关闭文件句柄。
这里思考一个问题,read是一次性的读取所有文件内容,但如果文件特别大的话,比如有两个G的日志文件,将这么大的文件一次性的读取到内存中,是不合理的,所以,可以在read的时候指定每次读的字节数大小:
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,还有其他的读方法:
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
模式——追加写:
f6 = open('t1.txt', 'a')
for i in "hello":
f6.write('{}{}'.format(i, '\n'))
f6.close()
追加写模式中,如果文件存在,则以追加的方式继续写入,如果文件不存在就创建文件,然后以追加的方式写入。
wb/ab/rb
之前处理的都是文本字符串,要处理音视频、图片等二进制类型的文件时,就需要搭配使用b
模式了,b
模式标识处理的是二进制文件:
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还提供了随机读取文件方法:
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])
方法意为把当前读(或写)的位置移动到由offset
和whence
定义的位置,offset
表示偏移的字节量,而whence
为可选参数,搭配offset
使用,表示从哪个位置开始偏移,0表示从文件开头开始,1代表从当前位置开始开始,2表示从文件末尾开始偏移。 而tell
方法则返回当前指针所在的位置。
关闭文件
我们为什么在对文件对象f操作完毕之后,都要去关闭它?
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
来完成。这无需多说了:
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语句:
with open(...) as f:
f.read()
open
函数的参数不变,关键字as
后面的文件对象f
可以自定义。
with
语句允许使用上下文管理器,上下文管理器则是支持__enter__
与__exit__
方法的对象。__enter__
方法没有参数,当程序执行到with
语句的时候被触发,返回值绑定在文件对象f
上。而__exit__
方法则有三个参数,包括异常类型、异常对象、异常回溯。现在无需深入了解这个三个参数,只需知道当with
语句执行完毕,也就是对文件的操作执行完毕,with
会自动执行__exit__
方法来关闭文件。
来个示例:
with open('t1.txt', 'w') as f:
f.write('with 语句真省事')
with open('t1.txt', 'r') as f:
f.read() # with 语句真省事
with语句无疑帮我们做了很大的工作,让我们专心于文件操作本身。
当然了,with语句的功能不仅限于此,它还有其他的用法,这里不做过多的探讨。
f是什么?
我们对一个文件操作,总是要拿到这个文件句柄来做操作,那么这个文件句柄是什么呢?
from collections import Iterable
f = open('t1.txt', 'r')
print(isinstance(f, Iterable)) # True
由上例可以看到,文件句柄f
它也是一个可迭代对象,那么我们就可以通过循环来取值。
f = open('t1.txt', 'r')
for i in f:
print(i)
"""
hello
world
"""
每次循环读取一行数据。