Python基础手册16——文件
一、文件对象
文件对象是Python代码访问、操作磁盘上文件的主要接口。文件对象不仅可以用来访问普通的磁盘文件,而且也可以访问任何其它类型抽象层面上的 "文件"。一旦设置了合适的"钩子",你就可以访问具有文件类型接口的其它对象,就好像访问的是普通文件一样。进行这种抽象处理的主要原因是许多的输入/输出数据结构更趋向于使用通用的接口。这样就可以在程序行为和实现上保持一致性。
文件只是连续的字节序列(字符串),数据的传输经常会用到字节流,无论字节流是由单个字节还是大块数据组成。对Python而言,文件的内容总是字符串,无论文件包含的数据是什么类型。
二、内建函数 open()
没有特定的常量语法(也就是想创建列表那样使用符号表达式 [ ]
)创建文件对象。要创建一个文件对象,需要调用内建函数 open() ,内建函数 open() 提供了初始化输入/输出(I/O)操作的通用接口。 open() 内建函数成功打开文件后时候会返回一个文件对象,对该文件进行后继相关的操作都要用到这个对象,打开不成功会引发一个 IOError
异常。成功返回的文件对象可以作为计算机上的一个文件链接,你可以通过调用返回的文件对象的方法来读写相关外部文件。
内建函数 open() 的基本语法是:
file = open( file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None )
file 是包含要打开的文件名字的字符串,它可以是相对路径或者绝对路径。没有目录路径时,文件假定存在于当前的工作目录中(也就是脚本运行的地方)。
可选变量 mode 也是一个字符串,代表文件打开的模式,默认为 'r'
。通常,文件使用模式 'r'
,'w'
,或是 'a'
模式来打开,分别代表读取,写入和追加。还有个 'U'
模式,代表通用换行符支持。在模式字符串尾部加上 'b'
,可以进行二进制数据处理。加上 '+'
意味着同时以读写模式打开文件。
使用 'r' 或 'U' 模式打开的文件必须是已经存在的。使用 'w' 模式打开的文件若存在则首先清空,然后(重新)创建。以 'a' 模式打开的文件是为追加数据作准备的,所有写入的数据都将追加到文件的末尾,即使你 seek 到了其它的地方。如果文件不存在,将被自动创建,类似以 'w' 模式打开文件。
另外一个可选参数 buffering 用于指示访问文件所采用的缓冲方式。其中 0
表示不缓冲,1
表示只缓冲一行数据,任何其它大于 1 的值代表使用给定值作为缓冲区大小。不提供该参数或者给定负值代表使用系统默认缓冲机制,既对任何类电报机( tty )设备使用行缓冲,其它设备使用正常缓冲。一般情况下使用系统默认方式即可。
encoding 参数可以指定我们打开文件所使用的编码方式,比如 utf-8 或者 gbk。请保证使用文件本身的编码方式打开,否则会出现乱码,二进制文件默认生成 bytes 字符串类型,不能指定 encoding 参数。
如果你不知道文件的编码,可以使用第三方的 chardet 模块的 detect() 函数来帮助推测文件的编码格式。使用detect() 函数,你需要首先使用 ‘rb'模式也就是以二进制模型打开文件,然后文件中读取的bytes格式的字符串传递给detect()函数,detect() 函数会将推测的结果作为函数的结果返回。
返回的 confidence 指出了判断的准确程度,encoding指出了这个函数判断的文件的编码格式。
文件对象的访问模式
文件模式 | 操作 |
---|---|
r | 以只读方式打开 |
rU | 或 Ua 以读方式打开, 同时提供通用换行符支持 (PEP 278) |
w | 以写方式打开 ,如果文件存在就先清空文件,如果不存在就创建新文件 |
a | 以追加模式打开 (从 EOF 开始, 必要时创建新文件) |
+ | 以读写模式打开 |
w+ | 以读写模式打开 (参见 w ) |
a+ | 以读写模式打开 (参见 a ) |
rb | 以二进制读模式打开 |
wb | 以二进制写模式打开 (参见 w ) |
ab | 以二进制追加模式打开 (参见 a ) |
rb+ | 以二进制读写模式打开 (参见 r+ ) |
wb+ | 以二进制读写模式打开 (参见 w+ ) |
ab+ | 以二进制读写模式打开 (参见 a+ ) |
x | 文件存在报错,不存在就创建写内容 |
这里是一些打开文件的例子:
fp = open('/etc/motd') # 以读方式打开
fp = open('test', 'w') # 以写方式打开
fp = open('data', 'r+') # 以读写方式打开
fp = open(r'c:\io.sys', 'rb') # 以二进制读模式打开
在任何情况下,Python程序中处理的文本格式文件都采用字符串的形式。读取文件时会返回字符串形式的文本,文本作为字符串传递给 write() 方法。Python不会主动把对象转换为字符串——你必须传递一个已经格式化的字符串。
文本文件和二进制文件
在Python 3中文件类型都由 open() 函数的第二个参数决定,模式字符串包含一个 'b' 表示以二进制格式打开。Python总是支持文本文件和二进制文件。文本文件把内容表示为常规的 str 字符串,自动执行 Unicode 编码和解码,并且默认执行末行转换。二进制文件把内容表示为一个特殊的 bytes 字符串类型,并且允许程序不修改的访问文件内容。
通常,你必须使用 bytes 字符串处理二进制文件,使用常规的 str 字符串处理文本文件。此外,由于文本文件实现了Unicode编码,不能以文本模式打开一个二进制数据文件,这样会将其内容解码为Unicode文本,生成乱码,导致失败。此外二进制模式不会对数据执行任何末行转换,他直接读取硬盘中文件保存的二进制(0101)并以十六进制的格式显示。
通用换行符支持(UNS)
不同平台用来表示行结束的符号是不同的,例如 \n,\r,或者 \r\n。所以,Python 的解释器也要处理这样的任务,特别是在导入模块时分外重要。
这就是 UNS 的关键所在,作为 PEP 278 的结果,Python 2.3 引入了 UNS。当你使用 'U' 标志打开文件的时候,所有的行分割符(或行结束符,无论它原来是什么)通过 Python 的输入方法(例如 read*() )返回时都会被替换为换行符 NEWLINE(\n)。('rU' 模式也支持 'rb' 选项) 。这个特性还支持包含不同类型行结束符的文件。文件对象的 newlines 属性会记录它曾“看到的”文件的行结束符。
如果文件刚被打开,程序还没有遇到行结束符,那么文件的 newlines 为 None。在第一行被读取后,它被设置为第一行的结束符。如果遇到其它类型的行结束符,文件的 newlines 会成为一个包含每种格式的元组。注意 UNS 只用于读取文本文件。没有对应的处理文件输出的方法。在编译 Python 的时候,UNS 默认是打开的。如果你不需要这个特性,在运行 configure 脚本时,你可以使用 --without-universal-newlines 开关关闭它。如果你非要自己处理行结束符,使用 os 模块的相关属性。
使用 with open 打开文件,代码块执行完,文件都自动关闭
三、文件对象的内建方法
open() 成功执行并返回一个文件对象之后,所有对该文件的后续操作都将通过这个"句柄"(文件对象)进行。文件方法可以分为四类:输入,输出,文件内移动,以及杂项操作。
1、输入
read(self, n: int = -1) -> AnyStr
readline(self, limit: int = -1) -> AnyStr
readlines(self, hint: int = -1) -> List[AnyStr]
read() 方法用来直接读取文件对象并返回字符串,最多读取给定数目个字符(注意不是字节)。如果没有给定 n 参数(默认值为 -1)或者 n 值为负,文件将被读取直至末尾。
readline() 方法读取打开文件的一行(读取下个行结束符之前的所有字节)。然后整行(包括行结束符)作为字符串返回。和 read() 相同,它也有一个可选的 limit 参数,默认为 -1,代表读至行结束符。如果提供了该参数,那么在超过 limit 个字节后会返回不完整的行。
readlines() 方法并不像其它两个输入方法一样返回一个字符串。它会读取所有(剩余的)行然后把它们作为一个字符串列表返回。它的可选参数hint 代表返回的最大字节大小。如果它大于 0 ,那么返回的所有行应该大约有 hint 字节(可能稍微大于这个数字,因为需要凑齐缓冲区大小)。
2、输出
write(self, s: AnyStr) -> int
writelines(self, lines: List[AnyStr]) -> None
write() 内建方法功能与 read() 和 readline() 相反。它把含有文本数据或二进制数据块的字符串写入到文件中去。
和 readlines() 一样,writelines() 方法是针对列表的操作,它接受一个字符串列表作为参数,将 它 们 写 入 文 件。行 结 束 符 并 不 会 被 自 动 加 入,所 以 如 果 需 要 的 话,你 必 须 在 调 用 writelines() 前给每行结尾加上行结束符。
注意这里并没有 "writeline()" 方法,因为它等价于使用以行结束符结尾的单行字符串调用 write() 方法。
3、判断是否可读可写
readable(self) -> bool
writable(self) -> bool
在Linux系统中一切皆文件,包括硬件设备都虚拟成一个文件,但是有些文件是不可读,所以我们可以使用 readable() 方法,判断文件是否可读。
我们可以使用 writable() 方法来判断一个文件对象是否可写。
4、文件内移动
seek(self, offset: int, whence: int = 0) -> int
seekable(self) -> bool
tell(self) -> int
seek() 方法(类似 C 中的 fseek() 函数)可以在文件中移动文件指针到不同的位置。offset 字节代表相对于某个位置偏移量,offset 指定的是字节数,acsii 编码一个字母为1个字节,gbk 编码一个汉字为2个字节,utf-8 编码一个汉字为3个字节。whence 参数为位置,默认值为 0,代表从文件开头算起(即绝对偏移量),1 代表从当前位置算起,2 代表从文件末尾算起。注意:在文本文件中,没有使用 b 模式选项打开的文件,只允许从文件头开始计算相对位置,从其他位置计算时就会引发异常。如果你是一个 C 程序员,并且使用过了 fseek(),那么应该知道 0,1,2 分别对应着常量 SEEK_SET
,SEEK_CUR
,以及 SEEK_END
。当人们打开文件进行读写操作的时候就会接触到 seek()方法。
我们使用 seek() 方法在文件内部移动,使用 tell() 方法展示我们的移动过程,使用 seekable() 方法判断文件是否可进行 seek 操作。
5、截断
truncate(self, size:int=None)->int
truncate() 默认将当前位置以后的字符(不包括当前位置)截断(删除地方)。
6、文件迭代
如果想一行一行的扫描一个文本文件,文件迭代器往往是最佳选择。以这种方式编码的时候,open() 临时创建的文件对象将自动在每次循环迭代的时候读入并返回一行。这种形式通常很容易编写,对于内存使用很好,并且比其他选项更快(当然根据有多少变量)。
一行一行访问文件很简单:
for eachLine in f:
pass
在这个循环里,eachLine 代表文本文件的一行(包括末尾的行结束符),你可以使用它做任何想做的事情。
文件迭代更为高效,而且写(和读)这样的 Python 代码更容易。7、把内存中文件的内容刷入到硬盘中
flush(self) -> None
调用文件对象的 flush() 方法可以把内存中的文件的内容写入到硬盘中。
8、文件关闭
close(self) -> None
closed(self) -> bool
我们可以调用文件对象的 close() 方法关闭文件来结束对它的访问。Python 垃圾收集机制也会在文件对象的引用计数降至零的时候自动关闭文件。这在文件只有一个引用时发生,例如 fp = open(...),然后 fp 在原文件显式地关闭前被赋了另一个文件对象。良好的编程习惯要求在重新赋另个文件对象前关闭这个文件。如果你不显式地关闭文件,那么你可能丢失输出缓冲区的数据。默认情况下,输出文件总是缓冲的,这意味着写入的文本可能不会立即自动从内存转换到硬盘——关闭一个文件,或者运行其 flush() 方法,迫使缓存的数据进入硬盘。
调用 flush() 方法会直接把内存缓冲区中的数据立刻写入文件,而不是被动地等待输出缓冲区被写入。使用 closed() 方法判断一个文件对象是否已经关闭了。
四、标准文件
一般说来,只要你的程序一执行,那么你就可以访问三个标准文件。它们分别是标准输入(一般是键盘)、标准输出(到显示器的缓冲输出)和标准错误(到屏幕的非缓冲输出)。这里所说的"缓冲"和"非缓冲"是指 open() 函数的第三个参数。这些文件沿用的是 C 语言中的命名,分别为stdin,stdout 和 stderr。我们说"只要你的程序一执行就可以访问这三个标准文件",意思是这些文件已经被预先打开了,只要知道它们的文件句柄就可以随时访问这些文件。
Python 中可以通过 sys 模块来访问这些文件的句柄。导入 sys 模块以后,就可以使用sys.stdin,sys.stdout 和 sys.stderr 访问。print() 语句通常是输出到 sys.stdout;而 input() 语句则通常从 sys.stdin 接受输入。
五、第三方模块
使用用pickle模块来存储Python的原生对象
如果你真的想存储Python原生对象,但又无法信赖文件的数据来源,Python标准库pickle模块是能够让我们直接在文件中存储几乎任何Python对象的高级工具,并不需要我们吧字符串转换来转换去。
文件中打包二进制数据的存储与解析
有些高级应用程序也需要处理打包的二进制数据,Python的标准库包含的struct模块能够构造并解析打包的二进制数据。从某种意义上说,它是另一个数据转换工具,它能够把文件中的字符串解读为二进制数据。
《Python基础手册》系列:
Python基础手册 1 —— Python语言介绍
Python基础手册 2 —— Python 环境搭建(Linux)
Python基础手册 3 —— Python解释器
Python基础手册 4 —— 文本结构
Python基础手册 5 —— 标识符和关键字
Python基础手册 6 —— 操作符
Python基础手册 7 —— 内建函数
Python基础手册 8 —— Python对象
Python基础手册 9 —— 数字类型
Python基础手册10 —— 序列(字符串)
Python基础手册11 —— 序列(元组&列表)
Python基础手册12 —— 序列(类型操作)
Python基础手册13 —— 映射(字典)
Python基础手册14 —— 集合
Python基础手册15 —— 解析
Python基础手册16 —— 文件
Python基础手册17 —— 简单语句
Python基础手册18 —— 复合语句(流程控制语句)
Python基础手册19 —— 迭代器
Python基础手册20 —— 生成器
Python基础手册21 —— 函数的定义
Python基础手册22 —— 函数的参数
Python基础手册23 —— 函数的调用
Python基础手册24 —— 函数中变量的作用域
Python基础手册25 —— 装饰器
Python基础手册26 —— 错误 & 异常
Python基础手册27 —— 模块
Python基础手册28 —— 模块的高级概念
Python基础手册29 —— 包