【Python学习手册(第四版)】学习笔记09.2-Python对象类型-文件

个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。


目录

文件

文件操作

open模式

打开文件

使用文件

实际应用

文本和二进制文件

在文件中存储并解析Python对象

用pickle存储Python的原生对象

文件中打包二进制数据的存储与解析

文件上下文管理器(with open/try finally)

其他文件工具


主要介绍文件对象,文件对象是处理文件常用的并且全能的工具。


文件

大多数人都熟悉文件的概念,也就是计算机中由操作系统管理的具有名字的存储区域

文件-这个主要内置对象类型提供了一种可以存取Python程序内部文件的方法。

内置open函数会创建一个Python文件对象,可以作为计算机上的一个文件链接。在调用open之后,你可以通过调用返回文件对象的方法来读写相关外部文件。

文件对象多少有些不寻常。它们不是数字、序列也不是对应。相反,文件对象只是常见文件处理任务输出模块。多数文件方法都与执行外部文件相关的文件对象的输入和输出有关,但其他文件方法可查找文件中的新位置、刷新输出缓存等。

文件操作

open模式

模式

可做操作

若文件不存在

是否覆盖

r

只读

error

-

r+

读写

error

T

w

只写

create

T

w+

读写

create

T

a

只写

create

F,尾部追加写

a+

读写

create

F,尾部追加写

wb

只写二进制字符串,写入bytes

create

T

rb

只读二进制字符串,返回bytes

error

-

打开文件

为了打开一个文件,程序会调用内置open函数,首先是外部名,接着是处理模式。

模式典型地用字符串'r'代表为输入打开文件(默认值),'w'代表为输出生成并打开文件,'a'代表为在文件尾部追加内容而打开文件。处理模式参数也可以指定为其他选项:

·在模式字符串尾部加上b可以进行二进制数据处理

·加上“+”意味着同时为输入和输出打开文件(也就是说,我们可以对相同文件对象进行读写,往往与对文件中的修改的查找操作配合使用)。

要打开的两个参数必须都是Python的字符串,第三个是可选参数,它能够用来控制输出缓存:传入“0”意味着输出无缓存(写入方法调用时立即传给外部文件)。

外部文件名参量可能包含平台特定的以及绝对或相对目录路径前缀。没有目录路径时,文件假定存在当前的工作目录中(也就是脚本运行的地方)。

使用文件

一旦存在一个文件对象,就可以调用其方法来读写相关的外部文件。在任何情况下,Python程序中的文本文件都采用字符串的形式。读取文件时会返回字符串形式的文本,文本作为字符串传递给write方法。

文件迭代器是最好的读取行工具

虽然表中的读写方法都是常用的,但是要记住,现在从文本文件读取文字行的最佳方式是根本不要读取该文件。在其他章节会讲到:文件也有个迭代器会自动地在for循环、列表解析或者其他迭代语句中对文件进行逐行读取。

内容是字符串,不是对象

注意从文件读取的数据回到脚本时是一个字符串。所以如果字符串不是你所需的,就得将其转换成其他类型的Python对象。

与print语句不同的是,当你把数据写入文件时,Python不会自动把对象转换为字符串——你必须传递一个已经格式化的字符串。

之前见过的处理文件时可以来回转换字符串和数字的工具迟早会派上用场(例如,int、float、str以及字符串格式表达式)。Python也包括一些高级标准库工具,它用来处理一般对象的存储(例如,pickle模块)以及处理文件中打包的二进制数据(例如,struct模块)。

close是通常选项

调用文件close方法将会终止对外部文件的连接。在Python中,一旦对象不再被引用,则这个对象的内存空间就会自动被收回。当文件对象被收回的时候,如果需要的话,Python也会自动关闭该文件。意味着不需要总是手动去关闭文件,尤其是对于不会运行很长时间的简单脚本。

另一方面,手动关闭调用没有任何坏处,而且在大型程序中通常是个很不错的习惯。手动进行文件close方法调用是我们需要养成的一个好习惯

文件是缓冲的并且是可查找的

关闭既释放了操作系统资源也清空了缓冲区。默认情况下,输出文件总是缓冲的,这意味着写入的文本可能不会立即自动从内存转换到硬盘——关闭一个文件,或者运行其flush方法,迫使缓存的数据进入硬盘。可以用额外的open参数来避免缓存,但是,这可能会影响到性能。Python文件也是在字节偏移的基础上随机访问的,它们的seek方法允许脚本跳转到指定的位置读取或写入。

实际应用

看一个能够说明文件处理原理的简单例子。首先为输出而打开一个新文件,写入一个字符串(以行终止符\n结束),之后关闭文件。

接下来,我们将会在输入模式下再一次打开同一文件,读取该行。注意第二个readline调用返回一个空字符串。这是Python文件方法告诉我们已经到达文件底部(文件的空行是含有新行符的字符串,而不是空字符串)。

下面是完整的交互模式会话:

>>> mf = open('myfile.txt','w')    #创建、打开文件写入模式
>>> mf.write('hello file\n')    #写入
11
>>> mf.write('goodbye file\n')
13
>>> mf.close()    #关闭文件

>>> mf = open('myfile.txt','r')    #打开读取
>>> mf.readline()    #读取行
'hello file\n'
>>> mf.readline()
'goodbye file\n'
>>> mf.readline()
''

Windows文本文件打开

文件write调用返回了写入的字符数,这个例子把一行文本写成字符串,包括行终止符\n。写入方法不会为我们添加行终止符,所以程序必须包含它来严格地终止行(否则,下次写入时会简单地延长文件的当前行)。

如果想要显示带有末行字符解释的文件内容,用文件对象的read方法把整个文件读入到一个字符串中,并打印它:

>>> open('myfile.txt').read()
'hello file\ngoodbye file\n'
>>> print(open('myfile.txt').read())
hello file
goodbye file

>>>

如果想要一行一行地扫描一个文本文件,文件迭代器往往是最佳选择:

>>> for line in open('myfile.txt'):
...     print(line)
...
hello file

goodbye file

>>>

以这种方式编码的时候,open临时创建的文件对象将自动在每次循环迭代的时候读入并返回一行。这种形式通常很容易编写,对于内存使用很好,并且比其他选项更快(当然,根据有多少变量)。

文本和二进制文件

·文本文件把内容表示为常规的str字符串,自动执行Unicode编码和解码,并且默认执行末行转换。

·二进制文件把内容表示为一个特殊的bytes字符串类型,并且允许程序不修改地访问文件内容

Python 3.0中的源自于简单文本和Unicode文本都合并为一种常规字符串类型,所有的文本都是Unicode,包括ASCII和其他的8位编码。

如果需要处理国际化应用程序或者面向字节的数据,必须使用bytes字符串处理二进制文件,并且用常规的str字符串处理文本文件。

由于文本文件实现了Unicode编码,不能以文本模式打开一个二进制数据文件——将其内容解码为Unicode文本可能会失败。

当你读取一个二进制数据文件的时候,得到了一个bytes对象——表示绝对字节值的较小整数的一个序列(可能会也可能不会对应为字符),其外观几乎与常规的字符串完全相同:

>>> da = open('data.bin','w')
>>> da.write('\x00\x00\x07spam\x00\x08')
9
>>> da.close()

>>> da = open('data.bin','rb').read()
>>> da
b'\x00\x00\x07spam\x00\x08'
>>> da[4:8]
b'pam\x00'

>>> bin(da[2])
'0b111'
>>> da[2]
7

二进制文件不会对数据执行任何末行转换;在根据转化写入和读取并实现Unicode编码的时候,文本文件默认地把所有形式和\n之间映射起来。

在文件中存储并解析Python对象

下面这个例子将把多种Python对象占用多行写入文本文件。需要注意的是,我们必须使用转换工具把对象转成字符串。注意文件数据在脚本中一定是字符串,而写入方法不会自动地替我们做任何向字符串格式转换的工作(对于空格,在这里我将从写方法中忽视字节计数返回值)。

>>> x,y,z = 43,44,45
>>> x
43
>>> s = 'spam'
>>> d = {'a':1,'b':2}
>>> l = [1,2,3]
>>>
>>> f = open('datafile.txt','w')
>>> f.write(s + '\n')
5
>>> f.write('%s,%s,%s\n' % (x,y,z))
9
>>> f.write(str(l) + '$' + str(d) + '\n')
27

>>> f.write(l)     #直接写报错,要转str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: write() argument must be str, not list
>>> f.write(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: write() argument must be str, not dict

>>> f.close()

一旦我们创建了文件就可以通过打开和读取字符串来查看文件的内容(单步操作)。

交互模式的回显给出了正确的字节内容,而print语句则会解释内嵌行终止符来给予用户满意的结果

>>> chars = open('datafile.txt').read()
>>> chars
"spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> print(chars)
spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}

如何把文本文件中的字符串转换成真正的Python对象?Python不会自动把字符串转换为数字或其他类型的对象,如果我们需要使用诸如索引、加法等普通对象工具,就得这么做。

>>> f = open('datafile.txt')

>>> line = f.readline()
>>> line
'spam\n'
>>> line.rstrip()
'spam'

对第一行来说,我们使用字符串rstrip方法去掉多余的行终止符;line[:-1]分片也可以,但是只有确定所有行都含有"\n"的时候才行(文件中最后一行有时候会没有)。这里读取了包含字符串的行。

现在,读取包含数字的下一行,并解析出(抽取出)该行中的对象:

>>> line = f.readline()
>>> line
'43,44,45\n'
>>> parts = line.split(',')
>>> parts
['43', '44', '45\n']

这里使用字符串split方法,从逗号分隔符的地方将整行断开,得到的结果就是含有个别数字的子字符串列表。

如果我们想对这些数字做数学运算,还需要把字符串转为整数

>>> int(parts[1])
44
>>> nums = [int(p) for p in parts]
>>> nums
[43, 44, 45]

int能够把数字字符串转换为整数对象,列表解析表达式也可以一次性对列表中的每个项使用调用。

注意:不一定非要运行rstrip来删除最后部分的"\n",int和一些其他转换方法会忽略数字旁边的空白。

要转换文件第三行所储存的列表和字典,我们可以运行eval这一内置函数,eval能够把字符串当作可执行程序代码(从技术上来讲,就是一个含有Python表达式的字符串)。

>>> line = f.readline()
>>> line
"[1, 2, 3]${'a': 1, 'b': 2}\n"

>>> part = line.split('$')
>>> part
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]

>>> eval(part[0])
[1, 2, 3]
>>> eval(part[1])
{'a': 1, 'b': 2}
>>> ob = [eval(p) for p in part]
>>> ob
[[1, 2, 3], {'a': 1, 'b': 2}]

所有这些解析和转换的最终结果是一个普通的Python对象列表,而不是字符串。现在我们可以在脚本内应用列表和字典操作了。

用pickle存储Python的原生对象

使用eval可以把字符串转换成对象,它是一个功能强大的工具。事实上,它有时太过于强大。eval会高高兴兴地执行Python的任何表达式,甚至是有可能会删除计算机上所有文件的表达式,只要给予必要的权限。

如果你真的想储存Python原生对象,但又无法信赖文件的数据来源,Python标准库pickle模块会是个理想的选择。

pickle模块是能够直接在文件中存储几乎任何Python对象的高级工具,也并不要求我们把字符串转换来转换去。就像是超级通用的数据格式化和解析工具。

例如,想要在文件中储存字典,就直接用pickle来储存。

>>> d = {'a':1,'b':2}
>>> f = open('dafile.pk1','wb')
>>> import pickle
>>> pickle.dump(d,f)

>>> f.close()

将来想要取回字典时,只要简单地再用一次pickle进行重建就可以了:

>>> f = open('dafile.pk1','rb')
>>> e = pickle.load(f)
>>> e
{'a': 1, 'b': 2}

这么一看是不是比自己str、int、eval转方便多了。

我们取回等价的字典对象,没有手动断开或转换的要求。pickle模块执行所谓的对象序列化(object serialization),也就是对象和字节字符串之间的相互转换。要我们做的工作却很少。

事实上,pickle内部将字典转成字符串形式,不过这其实没什么可看的:

>>> open('dafile.pk1','rb').read()
b'\x80\x04\x95\x11\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x01a\x94K\x01\x8c\x01b\x94K\x02u.'

因为pickle能够依靠这一格式重建对象,我们不必自己手动来处理。有关pickle模块的更多内容可以参考Python标准库手册,或者在交互模式下输入pickle传给help来查阅相关信息。

也可以顺便看一看shelve模块。shelve用pickle把Python对象存放到按键访问的文件系统中。

注意:我们以二进制模式打开用来存储pickle化的对象的文件,二进制模式总是Python 3.0中必需的,因为pickle程序创建和使用一个bytes字符串对象,并且这些对象意味着二进制模式文件(文本模式文件意味着Python 3.0中的str字符串)。

Python 2.6也有一个cPickle模块,它是pickle的一个优化版本,可以直接导入以提高速度。Python 3.0把这模块改名为_pickle,并且在pickle中自动使用它——脚本直接导入pickle并且允许Python优化它。

文件中打包二进制数据的存储与解析

有些高级应用程序也需要处理打包的二进制数据,这些数据可能是C语言程序生成的。

Python的标准库中包含一个能够在这一范围起作用的工具:struct模块能够构造并解析打包的二进制数据。从某种意义上说,它是另一个数据转换工具,它能够把文件中的字符串解读为二进制数据。

例如,要生成一个打包的二进制数据文件,用'wb'(写入二进制)模式打开它,并将一个格式化字符串和几个Python对象传给struct。这里用的格式化字符串是指一个4字节整数、一个包含4个字符的字符串以及一个2位整数的数据包,所有这些都按照高位在前(big-endian)的形式(其他格式代码能够处理补位字节、浮点数等)。

>>> f = open('data.bin','wb')
>>> import struct

>>> data = struct.pack('>i4sh',7,b'life',8) #注意字符串前要加b
>>> data
b'\x00\x00\x00\x07life\x00\x08'
>>> f.write(data)
10
>>> f.close()

Python生成一个我们通常写入文件的二进制数据字符串。要将值解析为普通Python的对象,可以简单地读取字符串,并使用相同格式的字符串把它解压出来就可以了。Python能够把值提取出来转换为普通的Python对象(整数和字符串)。

>>> f = open('data.bin','rb')
>>> data = f.read()

>>> va = struct.unpack('>i4sh',data)
>>> va
(7, b'life', 8)

>>> st = va[1].decode('utf-8')
>>> st
'life'

注意这里的b'life' 是二进制文件,要用decode转为字符串。

二进制数据文件在这不再讲更多的细节了。如果需要更多帮助,可以参考Python库手册或者在交互模式下将struct传给help函数查阅相关的信息。同时需要注意的是,一般来说,二进制文件处理模式'wb'和'rb'可用于处理更简单的二进制文件。

文件上下文管理器(with open/try finally)

文件上下文管理器比文件自身多了一个异常处理功能,它允许我们把文件处理代码包装到一个逻辑层中,以确保在退出后可以自动关闭文件,而不是依赖于垃圾收集上的自动关闭:

>>> with open(r'C:\Users\54242\myfile.txt') as myfile:    #注意前面的r保证路径
...     for line in myfile:
...             print(line)
...
hello file

goodbye file

>>>

try/finally语句可以提供类似的功能,但是,需要一些额外代码的成本——更准确地说,是3个额外的代码行(尽管我们常常可以避免两个选项,并且使用Python为我们自动关闭文件):

>>> myfile = open(r'C:\Users\54242\myfile.txt')
>>> try:
...     for line in myfile:
...             print(line)
... finally:
...     myfile.close()
...
hello file

goodbye file

>>>

open()使用后需close(),with open()则不需要,会自动关闭。

其他文件工具

还有很多并没有列出。例如,seek函数能够复位你在文件中的当前位置(下次读写将应用在该位置上),flush能够强制性地将缓存输出写入磁盘(文件总会默认进行缓存)等。

同样,需要注意的是,虽然open函数及其返回的文件对象是Python脚本中通向外部文件的主要接口,Python工具集中还有其他类似的文件工具,还有其他可用的,例如:

标准流

在sys模块中预先打开的文件对象,例如sys.stdout

os模块中的描述文件

处理整数文件,支持诸如文件锁定之类的较低级工具。

sockets、pipes和FIFO文件

文件类对象,用于同步进程或者通过网络进行通信。

通过键来存取的文件

通过键直接存储的不变的Python对象。

Shell命令流

像os.popen和subprocess.Popen这样的工具,支持产生shell命令,并读取和写入到标准流。

第三方开源领域提供了甚至更多的文件类工具,包括PySerial扩展中支持与窗口交流,以及pexpect系统中的交互程序。

用来实现文件对象的类位于标准库模块io中。可以参见这个模块的文档,或者编写可以使用它来定制的类,并且在打开的文件F上运行一条type(F)调用,以得到相关提示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兴焉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值