# 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 的條款和條件