1. 程式人生 > >Python 核心編程(第二版)——文件和輸入輸出

Python 核心編程(第二版)——文件和輸入輸出

第二版 next 相對 指定 chdir 同方 size 替換 n)

1.文件對象

文件對象不僅可以用來訪問普通的磁盤文件, 而且也可以訪問任何其它類型抽象層面上的"文件". 一旦設置了合適的"鉤子", 你就可以訪問具有文件類型接口的其它對象, 就好像訪問的是普通文件一樣。文件只是連續的字節序列. 數據的傳輸經常會用到字節流, 無論字節流是由單個字節還是大塊數據組成。

2.文件內建函數【open()和 file()】

作為打開文件之門的"鑰匙", 內建函數 open() [以及 file() ]提供了初始化輸入/輸出(I/O)操作的通用接口。

open() 內建函數成功打開文件後時候會返回一個文件對象, 否則引發一個錯誤。當操作失敗, Python 會產生一個 IOError 異常。內建函數 open() 的基本語法是:

file_object = open(file_name, access_mode=‘r‘, buffering=-1)

file_name 是包含要打開的文件名字的字符串, 它可以是相對路徑或者絕對路徑. 可選變量access_mode 也是一個字符串, 代表文件打開的模式. 通常, 文件使用模式 ‘r‘, ‘w‘, 或是 ‘a‘模式來打開, 分別代表讀取, 寫入和追加. 還有個 ‘U‘ 模式, 代表通用換行符支持。使用 ‘r‘ 或 ‘U‘ 模式打開的文件必須是已經存在的. 使用 ‘w‘ 模式打開的文件若存在則首先清空, 然後(重新)創建. 以 ‘a‘ 模式打開的文件是為追加數據作準備的, 所有寫入的數據都將追加到文件的末尾. 即使你 seek 到了其它的地方. 如果文件不存在, 將被自動創建, 類似以 ‘w‘模式打開文件.

如果沒有給定 access_mode , 它將自動采用默認值 ‘r‘ 。另外一個可選參數 buffering 用於指示訪問文件所采用的緩沖方式. 其中 0 表示不緩沖, 1表示只緩沖一行數據, 任何其它大於 1 的值代表使用給定值作為緩沖區大小. 不提供該參數或者給定負值代表使用系統默認緩沖機制, 既對任何類電報機( tty )設備使用行緩沖, 其它設備使用正常緩沖. 一般情況下使用系統默認方式即可.

文件對象的訪問模式
文件模式 操作
r 以讀方式打開
rU 或 Ua 以讀方式打開, 同時提供通用換行符支持 (PEP 278)
w 以寫方式打開 (必要時清空)
a 以追加模式打開 (從 EOF 開始, 必要時創建新文件)
r+ 以讀寫模式打開
w+ 以讀寫模式打開 (參見 w )
a+ 以讀寫模式打開 (參見 a )
rb 以二進制讀模式打開
wb 以二進制寫模式打開 (參見 w )
ab 以二進制追加模式打開 (參見 a )
rb+ 以二進制讀寫模式打開 (參見 r+ )
wb+ 以二進制讀寫模式打開 (參見 w+ )
ab+ 以二進制讀寫模式打開 (參見 a+ )
a Python 2.3 中新增

open() 和 file() 函數具有相同的功能, 可以任意替換。一般說來, 我們建議使用 open() 來讀寫文件。

通用換行符支持(UNS):當你使用 ‘U‘ 標誌打開文件的時候, 所有的行分割符(或行結束符, 無論它原來是什麽)通過 Python 的輸入方法(例如 read*() )返回時都會被替換為換行符NEWLINE(\n). (‘rU‘ 模式也支持 ‘rb‘ 選項) . 這個特性還支持包含不同類型行結束符的文件. 文件對象的newlines 屬性會記錄它曾“看到的”文件的行結束符。

註意 UNS 只用於讀取文本文件, 沒有對應的處理文件輸出的方法。

在編譯 Python 的時候,UNS 默認是打開的. 如果你不需要這個特性, 在運行configure 腳本時,你可以使用 --without-universal-newlines 開關關閉它。

3.文件內建方法

open() 成功執行並返回一個文件對象之後, 所有對該文件的後續操作都將通過這個"句柄"進行. 文件方法可以分為四類: 輸入, 輸出, 文件內移動, 以及雜項操作.

1)輸入

read() 方法用來直接讀取字節到字符串中, 最多讀取給定數目個字節. 如果沒有給定 size參數(默認值為 -1)或者 size 值為負, 文件將被讀取直至末尾.

readline() 方法讀取打開文件的一行(讀取下個行結束符之前的所有字節). 然後整行,包括行結束符,作為字符串返回.它也有一個可選的 size 參數, 默認為 -1, 代表讀至行結束符. 如果提供了該參數, 那麽在超過size 個字節後會返回不完整的行.

readlines() 方法並不像其它兩個輸入方法一樣返回一個字符串. 它會讀取所有(剩余的)行然後把它們作為一個字符串列表返回. 它的可選參數 sizhint 代表返回的最大字節大小. 如果它大於 0 , 那麽返回的所有行應該大約有 sizhint 字節(可能稍微大於這個數字, 因為需要湊齊緩沖區大小).

2)輸出

write() 內建方法把含有文本數據或二進制數據塊的字符串寫入到文件中去。

writelines() 方法是針對列表的操作, 它接受一個字符串列表作為參數, 將它們寫入文件. 行結束符並不會被自動加入, 所以如果需要的話, 你必須在調用writelines()前給每行結尾加上行結束符.

核心筆記:保留行分隔符。當使用輸入方法如 read() 或者 readlines() 從文件中讀取行時, Python 並不會刪除行結束符. 這個操作被留給了程序員.輸出方法 write() 或 writelines() 也不會自動加入行結束符. 你應該在向文件寫入數據前自己完成。

3)文件內移動

seek() 方法可以在文件中移動文件指針到不同的位置。offset字節代表相對於某個位置偏移量. 位置的默認值為 0 , 代表從文件開頭算起(即絕對偏移量), 1 代表從當前位置算起, 2 代表從文件末尾算起.

text() 方法是對 seek() 的補充; 它告訴你當前文件指針在文件中的位置 - 從文件起始算起,單位為字節.

4)文件叠代

在 Python 2.2 中引進了叠代器和文件叠代,文件對象成為了它們自己的叠代器,可以使用叠代器的 next 方法, file.next() 可以用來讀取文件的下一行,文件叠代更為高效, 而且寫(和讀)這樣的 Python 代碼更容易.

5)其他

close() 通過關閉文件來結束對它的訪問. Python 垃圾收集機制也會在文件對象的引用計數降至零的時候自動關閉文件.

fileno() 方法返回打開文件的描述符. 這是一個整數, 可以用在如 os 模塊( os.read() )的一些底層操作上.

調用 flush() 方法會直接把內部緩沖區中的數據立刻寫入文件, 而不是被動地等待輸出緩沖區被寫入.

isatty() 是一個布爾內建函數, 當文件是一個類 tty 設備時返回 True , 否則返回False .

truncate() 方法將文件截取到當前文件指針位置或者到給定 size , 以字節為單位.

6)文件方法雜項

核心筆記: 行分隔符和其它文件系統的差異。

操作系統間的差異之一是它們所支持的行分隔符不同.在 POSIX (Unix 系列或 Mac OS X)系統上, 行分隔符是 換行符 NEWLINE ( \n ) 字符. 在舊的 MacOS 下是 RETURN ( \r ) , 而 DOS 和Wind32 系統下結合使用了兩者 ( \r\n ).

另個不同是路徑分隔符(POSIX 使用 "/", DOS 和 Windows 使用 "\", 舊版本的 MacOS 使用":"), 它用來分隔文件路徑名, 標記當前目錄和父目錄.

Python 的 os 模塊設計者已經幫我們想到了這些問題. os 模塊有五個很有用的屬性.

有助於跨平臺開發的OS模塊屬性
os 模塊屬性 描述

linesep

用於在文件中分隔行的字符串
sep 用來分隔文件路徑名的字符串
pathsep 用於分隔文件路徑的字符串
curdir 當前工作目錄的字符串名稱
pardir (當前工作目錄的)父目錄字符串名稱

不管你使用的是什麽平臺, 只要你導入了 os 模塊, 這些變量自動會被設置為正確的值, 減少了你的麻煩.

print 語句默認在輸出內容末尾後加一個換行符, 而在語句後加一個逗號就可以避免這個行為.readline() 和 readlines() 函數不對行裏的空白字符做任何處理,所以你有必要加上逗號. 如果你省略逗號, 那麽顯示出的文本每行後會有兩個換行符, 其中一個是輸入是附帶的, 另個是 print 語句自動添加的.

文件對象還有一個 truncate() 方法, 它接受一個可選的 size 作為參數. 如果給定, 那麽文件將被截取到最多 size 字節處. 如果沒有傳遞 size 參數, 那麽默認將截取到文件的當前位置.如, 你剛打開了一個文件, 然後立即調用 truncate() 方法, 那麽你的文件(內容)實際上被刪除,這時候你是其實是從 0 字節開始截取的( tell() 將會返回這個數值 ).

技術分享
1 filename = input(Enter file name: )
2 fobj = open(filename, w)
3 while True:
4     aLine = input("Enter a line (‘.‘ to quit): ")
5     if aLine != ".":
6         fobj.write(%s%s % (aLine, os.linesep)
7     else:
8         break
9 fobj.close()
View Code 技術分享
 1 >>> f = open(/tmp/x, w+)
 2 >>> f.tell()
 3 0
 4 >>> f.write(test line 1\n) # 加入一個長為12 的字符串 [0-11]
 5 >>> f.tell()
 6 12
 7 >>> f.write(test line 2\n) # 加入一個長為12 的字符串 [12-23]
 8 >>> f.tell() # 告訴我們當前的位置
 9 24
10 >>> f.seek(-12, 1) # 向後移12 個字節
11 >>> f.tell() # 到了第二行的開頭
12 12
13 >>> f.readline()
14 test line 2\012
15 >>> f.seek(0, 0) # 回到最開始
16 >>> f.readline()
17 test line 1\012
18 >>> f.tell() # 又回到了第二行
19 12
20 >>> f.readline()
21 test line 2\012
22 >>> f.tell() # 又到了結尾
23 24
24 >>> f.close() # 關閉文件
View Code

file.seek(off, whence=0):從文件中移動off個操作標記(文件指針),正往結束方向移動,負往開始方向移動。如果設定了whence參數,就以whence設定的起始位為準,0代表從頭開始,1代表當前位置,2代表文件最末尾位置。[註:文件需以b模式打開才可以進行文件尾計算]

文件對象的內建方法列表
文件對象的方法 操作
file.close() 關閉文件
file.fileno() 返回文件的描述符(file descriptor ,FD, 整數值)
file.flush() 刷新文件的內部緩沖區
file.isatty() 判斷 file 是否是一個類 tty 設備
file.next()

返回文件的下一行(類似於file.readline() ), 或在沒有其它行時引發 StopIteration 異常

file.read(size=-1)

從文件讀取 size 個字節, 當未給定 size 或給定負值的時候, 讀取剩余的所有字節, 然後作為字符串返回

file.readinto(buf,size)

從文件讀取 size 個字節到 buf 緩沖器(已不支持)

file.readline(size=-1) 從文件中讀取並返回一行(包括行結束符), 或返回最大 size個字符
file.readlines(sizhint=0) 讀取文件的所有行並作為一個列表返回(包含所有的行結束符); 如果給定 sizhint 且大於 0 , 那麽將返回總和大約為sizhint 字節的行(大小由緩沖器容量的下一個值決定)( 比如說緩沖器的大小只能為4K 的倍數,如果sizhint 為15k,則最後返回的可能是16k———譯者按)
file.xreadlines() 用於叠代, 可以替換 readlines() 的一個更高效的方法
file.seek(off,whence=0) 在文件中移動文件指針, 從 whence ( 0 代表文件其始, 1 代表當前位置, 2 代表文件末尾)偏移 off 字節
file.tell() 返回當前在文件中的位置
file.truncate(size=file.tell()) 截取文件到最大 size 字節, 默認為當前文件位置
file.write(str) 向文件寫入字符串
file.writelines(seq) 向文件寫入字符串序列 seq ; seq 應該是一個返回字符串的可叠代對象; 在 2.2 前, 它只是字符串的列表

4.文件內建屬性

文件對象的屬性
文件對象的屬性 描述
file.closed True 表示文件已經被關閉, 否則為 False
file.encoding 文件所使用的編碼 - 當 Unicode 字符串被寫入數據時, 它們將自動使用 file.encoding 轉換為字節字符串; 若file.encoding 為 None 時使用系統默認編碼
file.mode 文件打開時使用的訪問模式
file.name 文件名
file.newlines 未讀取到行分隔符時為 None , 只有一種行分隔符時為一個字符串, 當文件有多種類型的行結束符時,則為一個包含所有當前所遇到的行結束符的列表
file.softspace 為0 表示在輸出一數據後,要加上一個空格符,1 表示不加。這個屬性一般程序員用不著,由程序內部使用。

5.標準文件

一般說來, 只要你的程序一執行, 那麽你就可以訪問三個標準文件. 它們分別是標準輸入(一般是鍵盤), 標準輸出(到顯示器的緩沖輸出)和標準錯誤(到屏幕的非緩沖輸出).(這裏所說的"緩沖"和"非緩沖"是指 open() 函數的第三個參數.)這些文件沿用的是 C 語言中的命名, 分別為stdin , stdout 和 stderr .

Python 中可以通過 sys 模塊來訪問這些文件的句柄. 導入 sys 模塊以後, 就可以使用sys.stdin , sys.stdout 和 sys.stderr 訪問. print 語句通常是輸出到 sys.stdout ; 而內建raw_input() 則通常從 sys.stdin 接受輸入.

記得 sys.* 是文件, 所以你必須自己處理好換行符. 而 print 語句會自動在要輸出的字符串後加上換行符。

6.命令行參數

sys 模塊通過 sys.argv 屬性提供了對命令行參數的訪問。 命令行參數是調用某個程序時除程序名以外的其它參數.

argc 和 argv 分別代表參數個數(argumentcount)和參數向量(argument vector). argv 變量代表一個從命令行上輸入的各個參數組成的字符串數組; argc 變量代表輸入的參數個數. 在 Python 中, argc 其實就是 sys.argv 列表的長度,而該列表的第一項 sys.argv[0] 永遠是程序的名稱.總結如下:

?? sys.argv 是命令行參數的列表
?? len(sys.argv) 是命令行參數的個數(也就是 argc)

Python 2.3 引入的 optparse 模塊輔助處理命令行參數,它是面向對象的。

7.文件系統

對文件系統的訪問大多通過 Python 的 os 模塊實現. 該模塊是Python 訪問操作系統功能的主要接口. os 模塊實際上只是真正加載的模塊的前端, 而真正的那個"模塊"明顯要依賴與具體的操作系統. 這個"真正"的模塊可能是以下幾種之一: posix (適用於 Unix 操作系統), nt (Win32),mac(舊版本的 MacOS), dos (DOS), os2 (OS/2), 等. 你不需要直接導入這些模塊. 只要導入 os 模塊, Python 會為你選擇正確的模塊, 你不需要考慮底層的工作.

模塊的文件、目錄訪問函數
函數 描述
文件處理
mkfifo()/mknod() 創建命名管道/創建文件系統節點
remove()/unlink() Delete file 刪除文件
rename()/renames() 重命名文件
*stat() 返回文件信息
symlink() 創建符號鏈接
utime() 更新時間戳
tmpfile() 創建並打開(‘w+b‘)一個新的臨時文件
walk() 生成一個目錄樹下的所有文件名
目錄/文件夾
chdir()/fchdir() 改變當前工作目錄/通過一個文件描述符改變當前工作目錄
chroot() 改變當前進程的根目錄
listdir() 列出指定目錄的文件
getcwd()/getcwdu() 返回當前工作目錄/功能相同, 但返回一個 Unicode 對象
mkdir()/makedirs() 創建目錄/創建多層目錄
rmdir()/removedirs() 刪除目錄/刪除多層目錄
訪問/權限
access() 檢驗權限模式
chmod() 改變權限模式
chown()/lchown() 改變 owner 和 group ID/功能相同, 但不會跟蹤鏈接
umask() 設置默認權限模式
文件描述符操作
open() 底層的操作系統 open (對於文件, 使用標準的內建 open() 函數)
read()/write() 根據文件描述符讀取/寫入數據
dup()/dup2() 復制文件描述符號/功能相同, 但是是復制到另一個文件描述符
設備號
makedev() 從 major 和 minor 設備號創建一個原始設備號
major() /minor() 從原始設備號獲得 major/minor 設備號

模塊 os.path 可以完成一些針對路徑名的操作. 它提供的函數可以完成管理和操作文件路徑名中的各個部分, 獲取文件或子目錄信息, 文件路徑查詢等操作.

os.path 模塊中的路徑名訪問函數
函數 描述
分隔
basename() 去掉目錄路徑, 返回文件名
dirname() 去掉文件名, 返回目錄路徑
join() 將分離的各部分組合成一個路徑名
split() 返回 (dirname(), basename()) 元組
splitdrive() 返回 (drivename, pathname) 元組
splitext() 返回 (filename, extension) 元組
信息
getatime() 返回最近訪問時間
getctime() 返回文件創建時間
getmtime() 返回最近文件修改時間
getsize() 返回文件大小(以字節為單位)
查詢
exists() 指定路徑(文件或目錄)是否存在
isabs() 指定路徑是否為絕對路徑
isdir() 指定路徑是否存在且為一個目錄
isfile() 指定路徑是否存在且為一個文件
islink() 指定路徑是否存在且為一個符號鏈接
ismount() 指定路徑是否存在且為一個掛載點
samefile() 兩個路徑名是否指向同個文件

核心模塊:os (和 os.path )。os 和 os.path 模塊提供了訪問計算機文件系統的不同方法.

8.永久存儲模塊

Python 提供了許多可以實現最小化永久性儲存的模塊. 其中的一組( marshal 和 pickle )可以用來轉換並儲存 Python 對象. 該過程將比基本類型復雜的對象轉換為一個二進制數據集合,這樣就可以把數據集合保存起來或通過網絡發送, 然後再重新把數據集合恢復原來的對象格式.這個過程也被稱為數據的扁平化, 數據的序列化, 或者數據的順序化. 另外一些模塊(dbhash/bsddb, dbm, gdbm, dumbdbm 等)以及它們的"管理器"( anydbm )只提供了 Python 字符串的永久性儲存. 而最後一個模塊( shelve ) 則兩種功能都具備.

marshal 和 pickle 模塊都可以對 Python 對象進行儲存轉換. 這些模塊本身並沒有提供"永久性儲存"的功能, 因為它們沒有為對象提供名稱空間, 也沒有提供對永久性儲存對象的並發寫入訪問( concurrent write access ). 它們只能儲存轉換 Python 對象, 為保存和傳輸提供方便. 數據儲存是有次序的(對象的儲存和傳輸是一個接一個進行的). marshal 和 pickle 模塊的區別在於 marshal 只能處理簡單的 Python 對象(數字, 序列, 映射, 以及代碼對象), 而pickle 還可以處理遞歸對象, 被不同地方多次引用的對象, 以及用戶定義的類和實例. pickle 模塊還有一個增強的版本叫 cPickle , 使用 C 實現了相關的功能.

*db* 系列的模塊使用傳統的 DBM 格式寫入數據, Python 提供了 DBM 的多種實現:dbhash/bsddb, dbm, gdbm, 以及 dumbdbm 等.如果你不確定的話, 那麽最好使用 anydbm 模塊, 它會自動檢測系統上已安裝的 DBM 兼容模塊, 並選擇"最好"Edit By VheavensEdit By Vheavens的一個. dumbdbm 模塊是功能最少的一個, 在沒有其它模塊可用時, anydbm 才會選擇它.

shelve 模塊使用 anydbm 模塊尋找合適的 DBM 模塊, 然後使用 cPickle 來完成對儲存轉換過程. shelve 模塊允許對數據庫文件進行並發的讀訪問, 但不允許共享讀/寫訪問.

技術分享

核心模塊: pickle 和 cPickle

可以使用 pickle 模塊把 Python 對象直接保存到文件裏, 而不需要把它們轉化為字符串,也不用底層的文件訪問操作把它們寫入到一個二進制文件裏. pickle 模塊會創建一個 Python 語言專用的二進制格式, 你不需要考慮任何文件細節, 它會幫你幹凈利索地完成讀寫對象操作, 唯一需要的只是一個合法的文件句柄.

pickle 模塊中的兩個主要函數是 dump() 和 load() . dump() 函數接受一個文件句柄和一個數據對象作為參數, 把數據對象以特定格式保存到給定文件裏. 當我們使用 load() 函數從文件中取出已保存的對象時, pickle 知道如何恢復這些對象到它們本來的格式. 我們建議你看一看pickle 和更"聰明"的 shelve 模塊, 後者提供了字典式的文件對象訪問功能, 進一步減少了程序員的工作.

9.相關模塊

文件相關模塊
模塊 內容
base64 提供二進制字符串和文本字符串間的編碼/解碼操作
binascii 提供二進制和ASCII 編碼的二進制字符串間的編碼/解碼操作
bz2 訪問 BZ2 格式的壓縮文件
csv 訪問 csv 文件(逗號分隔文件)
filecmp 用於比較目錄和文件
fileinput 提供多個文本文件的行叠代器
getopt/optparse 提供了命令行參數的解析/處理
glob/fnmatch 提供 Unix 樣式的通配符匹配的功能
gzip/zlib 讀寫 GNU zip( gzip) 文件(壓縮需要 zlib 模塊)
shutil 提供高級文件訪問功能
c/StringIO 對字符串對象提供類文件接口
tarfile 讀寫 TAR 歸檔文件, 支持壓縮文件
tempfile 創建一個臨時文件(名)
uu 格式的編碼和解碼
zipfile 用於讀取 ZIP 歸檔文件的工具

fileinput 模塊遍歷一組輸入文件, 每次讀取它們內容的一行, 類似 Perl 語言中的不帶參數的 "<>" 操作符. 如果沒有明確給定文件名, 則默認從命令行讀取文件名.glob 和 fnmatch 模塊提供了老式 Unix shell 樣式文件名的模式匹配, 例如使用星號( * )通配符代表任意字符串, 用問號( ? )匹配任意單個字符.

核心提示:使用os.path.expanduser() 的波浪號 ( ~ ) 進行擴展

雖然 glob 和 fnmatch 提供了 Unix 樣式的模式匹配, 但它們沒有提供對波浪號(用戶目錄)字符, ~ 的支持. 你可以使用 os.path.expanduser() 函數來完成這個功能, 傳遞一個帶波浪號的目錄, 然後它會返回對應的絕對路徑.Unix 家族系統還支持 "~user" 這樣的用法, 表示指定用戶的目錄. 還有, 註意 Win32版本函數沒有使用反斜杠來分隔目錄路徑.

gzip 和 zlib 模塊提供了對 zlib 壓縮庫直接訪問的接口. gzip 模塊是在 zlib 模塊上編寫的, 不但實現了標準的文件訪問, 還提供了自動的 gzip 壓縮/解壓縮. bz2 類似於 gzip , 用於操作 bzip 壓縮的文件.

Python 核心編程(第二版)——文件和輸入輸出