# 6.2. 与文件对象共事
* 6.2.1\. 读取文件
* 6.2.2\. 关闭文件
* 6.2.3\. 处理 I/O 错误
* 6.2.4\. 写入文件
Python 有一个内置函数,`open`,用来打开在磁盘上的文件。`open` 返回一个文件对象,它拥有一些方法和属性,可以得到被打开文件的信息,以及对被打开文件进行操作。
## 例 6.3. 打开文件
```
>>> f = open("/music/_singles/kairo.mp3", "rb")
>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.mode
'rb'
>>> f.name
'/music/_singles/kairo.mp3'
```
| | |
| --- | --- |
| \[1\] | `open` 方法可以接收三个参数:文件名、模式和缓冲区参数。只有第一个参数 (文件名) 是必须的;其它两个是[可选的](../power_of_introspection/optional_arguments.html "4.2. 使用可选参数和命名参数")。如果没有指定,文件以文本方式打开。这里我们以二进制方式打开文件进行读取。(`print open.__doc__` 会给出所有可能模式的很好的解释。) |
| \[2\] | `open` 函数返回一个对象 (到现在为止,[这一点应该不会使你感到吃惊](../getting_to_know_python/everything_is_an_object.html "2.4. 万物皆对象"))。一个文件对象有几个有用的属性。 |
| \[3\] | 文件对象的 `mode` 属性告诉你文件以何种模式被打开。 |
| \[4\] | 文件对象的 `name` 属性告诉你文件对象所打开的文件名。 |
## 6.2.1. 读取文件
你打开文件之后,你要做的第一件事是从中读取,正如下一个例子所展示的。
## 例 6.4. 读取文件
```
>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.tell()
0
>>> f.seek(-128, 2)
>>> f.tell()
7542909
>>> tagData = f.read(128)
>>> tagData
'TAGKAIRO****THE BEST GOA ***DJ MARY-JANE***
Rave Mix 2000http://mp3.com/DJMARYJANE \037'
>>> f.tell()
7543037
```
| | |
| --- | --- |
| \[1\] | 一个文件对象维护它所打开文件的状态。文件对象的 `tell` 方法告诉你在被打开文件中的当前位置。因为我们还没有对这个文件做任何事,当前位置为 `0`,它是文件的起始处。 |
| \[2\] | 文件对象的 `seek` 方法在被打开文件中移动到另一个位置。第二个参数指出第一个参数是什么意思:`0` 表示移动到一个绝对位置 (从文件起始处算起),`1` 表示移到一个相对位置 (从当前位置算起),还有 `2` 表示相对于文件尾的位置。因为我们搜索的 MP3 标记保存在文件的末尾,我们使用 `2` 并且告诉文件对象从文件尾移动到 `128` 字节的位置。 |
| \[3\] | `tell` 方法确认了当前位置已经移动了。 |
| \[4\] | `read` 方法从被打开文件中读取指定个数的字节,并且返回含有读取数据的字符串。可选参数指定了读取的最大字节数。如果没有指定参数,`read` 将读到文件末尾。(我们本可以在这里简单地说 `read()` ,因为我们确切地知道在文件的何处,事实上,我们读的是最后 128 个字节。) 读出的数据赋给变量 `tagData`,并且当前的位置根据所读的字节数作了修改。 |
| \[5\] | `tell` 方法确认了当前位置已经移动了。如果做一下算术,你会看到在读了 128 个字节之后,位置数已经增加了 128。 |
## 6.2.2. 关闭文件
打开文件消耗系统资源,并且其间其它程序可能无法访问它们 (取决于文件模式)。这就是一旦操作完毕就该关闭文件的重要所在。
## 例 6.5. 关闭文件
```
>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed
False
>>> f.close()
>>> f
<closed file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed
True
>>> f.seek(0)
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.tell()
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.read()
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.close()
```
| | |
| --- | --- |
| \[1\] | 文件对象的 `closed` 属性表示对象是打开还是关闭了文件。在本例中,文件仍然打开着 (`closed` 是 `False`)。 |
| \[2\] | 为了关闭文件,调用文件对象的 `close` 方法。这样就释放掉你加在文件上的锁 (如果有的话),刷新被缓冲的系统还未写入的输出 (如果有的话),并且释放系统资源。 |
| \[3\] | `closed` 属性证实了文件被关闭了。 |
| \[4\] | 文件被关闭了,但这并不意味着文件对象不再存在。变量 `f` 将继续存在,直到它[超出作用域](../object_oriented_framework/instantiating_classes.html#fileinfo.scope "例 5.8. 尝试实现内存泄漏")或被手工删除。然而,一旦文件被关闭,操作它的方法就没有一个能使用;它们都会引发异常。 |
| \[5\] | 对一个文件已经关闭的文件对象调用 `close` _不会_ 引发异常,它静静地失败。 |
## 6.2.3. 处理 I/O 错误
现在你已经足能理解前一章的例子程序 `fileinfo.py` 的文件处理代码了。下面这个例子展示了如何安全地打开文件和读取文件,以及优美地处理错误。
## 例 6.6. `MP3FileInfo` 中的文件对象
```
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
finally:
fsock.close()
.
.
.
except IOError:
pass
```
| | |
| --- | --- |
| \[1\] | 因为打开和读取文件有风险,并且可能引发异常,所有这些代码都用一个 `try...except` 块封装。(嘿,[标准化的缩近](../getting_to_know_python/indenting_code.html "2.5. 代码缩进")不好吗?这就是你开始欣赏它的地方。) |
| \[2\] | `open` 函数可能引发 `IOError` 异常。(可能是文件不存在。) |
| \[3\] | `seek` 方法可能引发 `IOError` 异常。(可能是文件长度小于 128 字节。) |
| \[4\] | `read` 方法可能引发 `IOError` 异常。(可能磁盘有坏扇区,或它在一个网络驱动器上,而网络刚好断了。) |
| \[5\] | 这是新的:一个 `try...finally` 块。一旦文件通过 `open` 函数被成功地打开,我们应该绝对保证把它关闭,即使是在 `seek` 或 `read` 方法引发了一个异常时。`try...finally` 块可以用来:在 `finally` 块中的代码将_总是_ 被执行,甚至某些东西在 `try` 块中引发一个异常也会执行。可以这样考虑,不管在路上发生什么,代码都会被 “即将灭亡” 地执行。 |
| \[6\] | 最后,处理我们的 `IOError` 异常。它可能是由调用 `open`、`seek` 或 `read` 引发的 `IOError` 异常。这里,我们其实不用关心,因为将要做的事就是静静地忽略它然后继续。(记住,`pass` 是一条不做任何事的 Python 语句。) 这样完全合法,“处理” 一个异常可以明确表示不做任何事。它仍然被认为处理过了,并且处理将正常继续,从 `try...except` 块的下一行代码开始。 |
## 6.2.4. 写入文件
正如你所期待的,你也能用与读取文件同样的方式写入文件。有两种基本的文件模式:
* 追加 (Append) 模式将数据追加到文件尾。
* 写入 (write) 模式将覆盖文件的原有内容。
如果文件还不存在,任意一种模式都将自动创建文件,因此从来不需要任何复杂的逻辑:“如果 log 文件还不存在,将创建一个新的空文件,正因为如此,你可以第一次就打开它”。打开文件并开始写就可以了。
## 例 6.7. 写入文件
```
>>> logfile = open('test.log', 'w')
>>> logfile.write('test succeeded')
>>> logfile.close()
>>> print file('test.log').read()
test succeeded
>>> logfile = open('test.log', 'a')
>>> logfile.write('line 2')
>>> logfile.close()
>>> print file('test.log').read()
test succeededline 2
```
| | |
| --- | --- |
| \[1\] | 你可以大胆地开始创建新文件 `test.log` 或覆盖现有文件,并为写入目的而打开它。(第二个参数 `"w"` 的意思是为文件写入而打开。) 是的,它和想象中的一样危险。我希望你不要关心文件以前的内容,因为它现在已经不存在了。 |
| \[2\] | 你可以使用 `open` 返回的文件对象的 `write` 方法向一个新打开的文件添加数据。 |
| \[3\] | `file` 是 `open` 的同义语。这一行语句打开文件,读取内容,并打印它们。 |
| \[4\] | 碰巧你知道 `test.log` 存在 (因为你刚向它写完了数据),所以你可以打开它并向其追加数据。(`"a"` 参数的意思是为追加目的打开文件。) 实际上即使文件不存在你也可以这样做,因为以追加方式打开一文件时,如果需要的话会创建文件。但是追加操作_从不_ 损坏文件的现有内容。 |
| \[5\] | 正如你所看到的,原来的行和你以追加方式写入的第二行现在都在 `test.log` 中了。同时注意两行之间并没包含回车符。因为两次写入文件时都没有明确地写入回车符,所以文件中没有包含回车符。你可以用 `"\n"` 写入回车符。因为你没做这项工作,所以你写到文件的所有内容都将显示在同一行上。 |
## 进一步阅读
* _Python Tutorial_ 讨论了文件的读取和写入,包括如何[将一个文件一次一行地读到 list 中](http://www.python.org/doc/current/tut/node9.html#SECTION009210000000000000000)。
* eff-bot 讨论了[各种各样读取文件方法](http://www.effbot.org/guides/readline-performance.htm) 的效率和性能。
* Python Knowledge Base 回答了[关于文件的常见问题](http://www.faqts.com/knowledge-base/index.phtml/fid/552)。
* _Python Library Reference_ 总结了[所有文件对象模块](http://www.python.org/doc/current/lib/bltin-file-objects.html)。
- 版权信息
- 第 1 章 安装 Python
- 1.1. 哪一种 Python 适合您?
- 1.2. Windows 上的 Python
- 1.3. Mac OS X 上的 Python
- 1.4. Mac OS 9 上的 Python
- 1.5. RedHat Linux 上的 Python
- 1.6. Debian GNU/Linux 上的 Python
- 1.7. 从源代码安装 Python
- 1.8. 使用 Python 的交互 Shell
- 1.9. 小结
- 第 2 章 第一个 Python 程序
- 2.1. 概览
- 2.2. 函数声明
- 2.3. 文档化函数
- 2.4. 万物皆对象
- 2.5. 代码缩进
- 2.6. 测试模块
- 第 3 章 内置数据类型
- 3.1. Dictionary 介绍
- 3.2. List 介绍
- 3.3. Tuple 介绍
- 3.4. 变量声明
- 3.5. 格式化字符串
- 3.6. 映射 list
- 3.7. 连接 list 与分割字符串
- 3.8. 小结
- 第 4 章 自省的威力
- 4.1. 概览
- 4.2. 使用可选参数和命名参数
- 4.3. 使用 type、str、dir 和其它内置函数
- 4.4. 通过 getattr 获取对象引用
- 4.5. 过滤列表
- 4.6. and 和 or 的特殊性质
- 4.7. 使用 lambda 函数
- 4.8. 全部放在一起
- 4.9. 小结
- 第 5 章 对象和面向对象
- 5.1. 概览
- 5.2. 使用 from _module_ import 导入模块
- 5.3. 类的定义
- 5.4. 类的实例化
- 5.5. 探索 UserDict:一个封装类
- 5.6. 专用类方法
- 5.7. 高级专用类方法
- 5.8. 类属性介绍
- 5.9. 私有函数
- 5.10. 小结
- 第 6 章 异常和文件处理
- 6.1. 异常处理
- 6.2. 与文件对象共事
- 6.3. for 循环
- 6.4. 使用 `sys.modules`
- 6.5. 与目录共事
- 6.6. 全部放在一起
- 6.7. 小结
- 第 7 章 正则表达式
- 7.1. 概览
- 7.2. 个案研究:街道地址
- 7.3. 个案研究:罗马字母
- 7.4. 使用 {n,m} 语法
- 7.5. 松散正则表达式
- 7.6. 个案研究:解析电话号码
- 7.7. 小结
- 第 8 章 HTML 处理
- 8.1. 概览
- 8.2. sgmllib.py 介绍
- 8.3. 从 HTML 文档中提取数据
- 8.4. BaseHTMLProcessor.py 介绍
- 8.5. locals 和 globals
- 8.6. 基于 dictionary 的字符串格式化
- 8.7. 给属性值加引号
- 8.8. dialect.py 介绍
- 8.9. 全部放在一起
- 8.10. 小结
- 第 9 章 XML 处理
- 9.1. 概览
- 9.2. 包
- 9.3. XML 解析
- 9.4. Unicode
- 9.5. 搜索元素
- 9.6. 访问元素属性
- 9.7. Segue [9]
- 第 10 章 脚本和流
- 10.1. 抽象输入源
- 10.2. 标准输入、输出和错误
- 10.3. 查询缓冲节点
- 10.4. 查找节点的直接子节点
- 10.5. 根据节点类型创建不同的处理器
- 10.6. 处理命令行参数
- 10.7. 全部放在一起
- 10.8. 小结
- 第 11 章 HTTP Web 服务
- 11.1. 概览
- 11.2. 避免通过 HTTP 重复地获取数据
- 11.3. HTTP 的特性
- 11.4. 调试 HTTP web 服务
- 11.5. 设置 User-Agent
- 11.6. 处理 Last-Modified 和 ETag
- 11.7. 处理重定向
- 11.8. 处理压缩数据
- 11.9. 全部放在一起
- 11.10. 小结
- 第 12 章 SOAP Web 服务
- 12.1. 概览
- 12.2. 安装 SOAP 库
- 12.3. 步入 SOAP
- 12.4. SOAP 网络服务查错
- 12.5. WSDL 介绍
- 12.6. 以 WSDL 进行 SOAP 内省
- 12.7. 搜索 Google
- 12.8. SOAP 网络服务故障排除
- 12.9. 小结
- 第 13 章 单元测试
- 13.1. 罗马数字程序介绍 II
- 13.2. 深入
- 13.3. romantest.py 介绍
- 13.4. 正面测试 (Testing for success)
- 13.5. 负面测试 (Testing for failure)
- 13.6. 完备性检测 (Testing for sanity)
- 第 14 章 测试优先编程
- 14.1. roman.py, 第 1 阶段
- 14.2. roman.py, 第 2 阶段
- 14.3. roman.py, 第 3 阶段
- 14.4. roman.py, 第 4 阶段
- 14.5. roman.py, 第 5 阶段
- 第 15 章 重构
- 15.1. 处理 bugs
- 15.2. 应对需求变化
- 15.3. 重构
- 15.4. 后记
- 15.5. 小结
- 第 16 章 函数编程
- 16.1. 概览
- 16.2. 找到路径
- 16.3. 重识列表过滤
- 16.4. 重识列表映射
- 16.5. 数据中心思想编程
- 16.6. 动态导入模块
- 16.7. 全部放在一起
- 16.8. 小结
- 第 17 章 动态函数
- 17.1. 概览
- 17.2. plural.py, 第 1 阶段
- 17.3. plural.py, 第 2 阶段
- 17.4. plural.py, 第 3 阶段
- 17.5. plural.py, 第 4 阶段
- 17.6. plural.py, 第 5 阶段
- 17.7. plural.py, 第 6 阶段
- 17.8. 小结
- 第 18 章 性能优化
- 18.1. 概览
- 18.2. 使用 timeit 模块
- 18.3. 优化正则表达式
- 18.4. 优化字典查找
- 18.5. 优化列表操作
- 18.6. 优化字符串操作
- 18.7. 小结
- 附录 A. 进一步阅读
- 附录 B. 五分钟回顾
- 附录 C. 技巧和窍门
- 附录 D. 示例清单
- 附录 E. 修订历史
- 附录 F. 关于本书
- 附录 G. GNU Free Documentation License
- G.0. Preamble
- G.1. Applicability and definitions
- G.2. Verbatim copying
- G.3. Copying in quantity
- G.4. Modifications
- G.5. Combining documents
- G.6. Collections of documents
- G.7. Aggregation with independent works
- G.8. Translation
- G.9. Termination
- G.10. Future revisions of this license
- G.11. How to use this License for your documents
- 附录 H. GNU 自由文档协议
- H.0. 序
- H.1. 适用范围和定义
- H.2. 原样复制
- H.3. 大量复制
- H.4. 修改
- H.5. 合并文档
- H.6. 文档合集
- H.7. 独立著作聚集
- H.8. 翻译
- H.9. 终止协议
- H.10. 协议将来的修订
- H.11. 如何为你的文档使用本协议
- 附录 I. Python license
- I.A. History of the software
- I.B. Terms and conditions for accessing or otherwise using Python
- 附录 J. Python 协议
- J.0. 关于译文的声明
- J.A. 软件的历史
- J.B. 使用 Python 的条款和条件