• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Python文件读写操作

Python编程 彭东稳 7年前 (2017-07-24) 20178次浏览 已收录 0个评论

一、Python 文件读写操作

Python 对文件的处理,对文件的读写是我们日常中最用的操作了,不管你是分析日志还是要将生成的结果写到文件里,都需要用到读文件的读写操作。读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

读操作

在 Python 下对文件操作非常容易,我们使用它的内建函数 open() 就可以打开一个文件,如果你要打开个文件一般的用法是:

open 方法可以接收三个参数:文件路径、打开模式和缓冲区大小。Python 3 未缓冲的文本 I/O 被禁用,貌似有个 Bug:issue17404

mode 说明 注意
r 只读方式打开文件 只能打开已存在的文件
w 只写方式打开文件 文件不存在会自动创建,文件存在则清空
a 追加方式打开 文件不存在会自动创建
a+ 追加和读写方式打开
rb、wb、ab 二进制方式打开

在模式后面使用'+'表示同时支持写入、输出操作,如 r+(如果以r+这种方式写入那么文件必须提前存在)、w+、a+。在模式后面附加 'b' 表示以二进制方式打开,如 rb+、wb+。这些模式基本覆盖了我们日常的对文件操作,当然除了这些还有一些其他模式,想深入了解的可以去自己学习下,如果是对运维来说这些基本够了。

如果文件打开成功,接下来,调用 read() 方法可以一次读取文件的全部内容(硬伤就是它会一次将读入文件的所有内容,所以读大文件,会把你内存吃满),Python 把内容读到内存,用一个 str 对象表示:

最后一步是调用 close() 方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:

由于文件读写时都有可能产生 IOError,一旦出错,后面的 close() 就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用 try ... finally 来实现:

但是每次都这么写实在太繁琐,所以,Python 引入了 with 语句来自动帮我们调用 close() 方法:这和前面的 try … finally 是一样的,但是代码更佳简洁,并且不必调用 close() 方法。

调用 read() 会一次性读取文件的全部内容,如果文件有 10G,内存就爆了,所以,要保险起见,可以反复调用 read(size) 方法,每次最多读取 size 个字节的内容。

也可以采用文件迭代的方式访问文件,这种方式应该是最常用的读取文件方式了,因为我们可以直接迭代文件对象,例子如下:

另外,可以使用 readline() 方法,这个函数在某些场景有估计会用到,它的作用就是每次从文件中读取一行出来,我们看个例子:

这个是每次读取一行,所以需要用到 while 循环,用 if 来判断文件是否已经结束,如果结束就跳出,否则打印改行。

正常读取文件,每一行的内容都是'context\n',如果此行为空则为'\n'。当文件读完之后,最后一行就会返回一个空字符串,而没有\n换行符。

readlines() 这个方法我用的不多,因为它也有个硬伤就是它返回一个列表,所以当文件足够大的时候,返回的列表就会异常的大,就会非常慢,不过它的好处就是可以快速释放文件资源,我们了解下就可以,对付一般文件还是可以的,大文件就会 OOM,例子如下:

file-like object

像 open() 函数返回的这种有个 read() 方法的对象,在 Python 中统称为 file-like Object。除了 file 外,还可以是内存的字节流,网络流,自定义流等等。file-like Object 不要求从特定类继承,只要写个 read() 方法就行。

StringIO 就是在内存中创建的 file-like Object,常用作临时缓冲。

二进制

前面讲的默认都是读取文本文件,并且是 ASCII 编码的文本文件。要读取二进制文件,比如图片、视频等等,用 ‘rb’ 模式打开文件即可:

字符编码

字符编码要读取非 ASCII 编码的文本文件,就必须以二进制模式打开,再解码。比如 GBK 编码的文件(VIM 编辑器下通过 set fileencoding=gbk 可以创建一个 gbk 编码文件):

如果每次都这么手动转换编码嫌麻烦(写程序怕麻烦是好事,不怕麻烦就会写出又长又难懂又没法维护的代码),Python 还提供了一个 codecs 模块帮我们在读文件时自动转换编码,直接读出 unicode:

Python 3 开始,要读取非 UTF-8 编码的文本文件,需要给 open() 函数传入 encoding 参数,使用上更加方便了。例如,读取 GBK 编码的文件:

遇到有些编码不规范的文件,你可能会遇到 UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open() 函数还接收一个 errors 参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:

写文件

写文件和读文件是一样的,唯一区别是调用 open() 函数时,传入标识符’ w’ 或者 ‘wb’ 表示写文本文件或写二进制文件:

你可以反复调用 write() 来写入文件,但是务必要调用 close() 来关闭文件。

当调用 write(str) 时,Python 解释器调用系统调用想把把内容写到磁盘,但是 Linux 内核有文件缓存机制,所以缓存到内核的缓存区,当调用 close() 或 flush() 时才会真正的把内容写到文件。忘记调用 close() 或 flush() 的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用 with 语句来得保险:

二、操作文件常用方法和属性

读相关方法

next():迭代,一行一行地读取数据。

readline(n)

读入若干行,n 代表读入的最长字节数。默认读取一行数据字节大小。

readlines()

返回文件中的所有行并生成列表,指针就会转向最后一个字节。

read(size)

一次读多少个字节数据。

seek(offset,whence=0)

用来操作文件移动指针的偏移位置。offset 表示开始的偏移量,也就是代表需要移动偏移的字节数。whence 表示给 offset 参数一个定义,表示要从哪个位置开始偏移;可选值,0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。

tell()

返回当前指针在文件中的位置,单位字节。

打开文件之后,首先从文件开头读(即 seek=0),第一行有数据,f.tell() 记住当前位置(即第一行最后的换行符),再次打开文件后从上一次 tell 的位置开始读数据,yield data 即输出第一行内容,这是一个生成器。

写相关方法

write(‘new line.’)

往文件中写入数据可以加\n换行,文件权限要为 W。

writelines()

往文件中写字符串序列。

close()

关闭文件同时会刷新内存数据到磁盘上。

flush()

刷新缓冲区将内存中的数据保存到磁盘上。

属性

closed

查看当前文件是否关闭。

name

查看文件名。

mode

查看当前文件打开方式。

三、普通读写与二进制读写有何不同?

不同之处有两个地方:

第一,使用 ‘r’ 的时候如果碰到 ‘0x1A’,就会视为文件结束,这就是 EOF。使用 ‘rb’ 则不存在这个问题。即,如果你用二进制写入再用文本读出的话,如果其中存在 ‘0X1A’,就只会读出文件的一部分。使用 ‘rb’ 的时候会一直读到文件末尾。

第二,对于字符串 x=’abc\ndef’,我们可用 len(x) 得到它的长度为7,\n我们称之为换行符,实际上是 ‘0X0A’。当我们用 ‘w’ 即文本方式写的时候,在 Windows 平台上会自动将 ‘0X0A’ 变成两个字符 ‘0X0D’,’0X0A’,即文件长度实际上变成 8。当用 ‘r’ 文本方式读取时,又自动的转换成原来的换行符。如果换成 ‘wb’ 二进制方式来写的话,则会保持一个字符不变,读取时也是原样读取。所以如果用文本方式写入,用二进制方式读取的话,就要考虑这多出的一个字节了。’0X0D’ 又称回车符。Linux 下不会变。因为 Linux 只使用 ‘0X0A’ 来表示换行。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (0)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!