文件對象
文件對象不僅可以用來訪問普通的磁盤文件,而且也可以訪問其它任何類型抽象層面上的“文件”。一旦設置了合適的“鉤子”,你就可以訪問文件類型接口的其它對象,就好像訪問的是普通文件一樣。
文件對象的處理要以來很多內建函數,還有很多函數會返回文件對象或者是類文件對象。進行這種軸向處理的主要原因是許多輸入/輸出數據結構更趨向于使用通用的接口。這樣就可以在程序行為和實現上保持一致性。文件只是連續的字節序列,數據傳輸經常會用到字節流,無論字節流是由單個字節還是大塊數據組成。
文件內建函數[open()和file()]
內建函數open()(和file())提供了初始化輸入/輸出(I/O)操作的通用接口。open()成功打開文件后會返回一個文件對象,否則引發一個錯誤。當操作失敗,Python會產生一個IOError異常。open()函數的基本語法是:
file_object = open(file_name, access_mode='r', buffering=-1)
file_name是包含要打開的文件名字的字符串,它可以是相對路徑或者絕對路徑??蛇x變量access_mode也是一個字符串,代表文件打開的模式。通常,文件使用模式'r','w',或是'a'模式來打開,分別代表讀取,寫入和追加。還有個'U'模式,代表通用換行符支持。
使用'r' 或 'U'模式打開的文件必須是已經存在的。使用'w'模式打開的文件若存在則首先清空,然后(重新)創建。以'a'模式打開的文件是為追加數據作準備的,所有寫入的數據都將追加到文件的末尾,即使你seek到了其它地方。如果文件不存在,將被自動創建,類似以'w'模式打開文件,和C庫函數fopen()使用的模式。
其它的fopen()支持的模式也可以再工作在Python的open()下。包括'+'表示可讀寫,'b'表示二進制模式訪問。當然,對于所有POSIX兼容的Unix系統來說,'b'是可有可無的,因為它們把所有的文件當成二進制文件,包括文本文件。
以下是文件訪問模式的詳細列表,如果沒有給定access_mode,它將自動采用默認值'r'。
| 文件模式 | 操作 |
| r | 以讀的方式打開 |
| rU或Ua | 以讀的方式 |
| w | 以寫方式打開(必要時清空) |
| a | 以追加模式打開(從EOF開始,必要時創建新文件) |
| r+ | 以讀寫模式打開 |
| w+ | 以讀寫模式打開(參見 w ) |
| a+ | 以讀寫模式打開(參見 a ) |
| rb | 以二進制讀模式打開 |
| wb | 以二進制寫模式打開(參見 w ) |
| ab | 以二進制追加模式打開(參見 a ) |
| rb+ | 以二進制讀寫模式打開 |
| wb+ | 以二進制讀寫模式打開(參見 w ) |
| ab+ | 以二進制讀寫模式打開(參見 a ) |
如:
fp = open('/etc/motd') # 以讀方式打開
fp = open('test', 'w') # 以寫方式打開
fp = open('data', 'r+') # 以讀寫方式打開
fp = open(r'c:/io.sys', 'rb') # 以二進制讀模式打開
file()和open()函數具有相同的功能,可以任意替換。
一般來說,我們建議使用open()來讀寫文件,在您想說明您在處理文件對象時使用file(),例如 if instance(f, file)
通用換行符支持(UNS)
在os模塊的一屬性可以再不同平臺下訪問文件,不同平臺用來表示行結束的符號是不同的,例如/n,/r,或者/r/n.我們希望用相同的方式處理所有的文件,所以我們使用了UNS。當使用'U'標志打開文件的時候,所有的行分割符通過Python的輸入方式返回時都會被替換為換行符NEWLINE(/n).這個特性還支持包含不同類型行結束符的文件。文件對象的newlines屬性會記錄它曾看到的文件行結束符。
如果文件剛被打開,程序還沒遇到行結束符,那么文件的newlines為None.在第一行被讀取后,它被設置成第一行的結束符,文件的newlines會成為一個包含每種格式的元組。注意UNS只用于讀取文本文件。沒有對應的處理文件輸出的方法。
在編譯Python的時候,UNS默然打開,如果不需要這個特性,在運行configure腳本時,可以用——without—universal—newlines開關關閉它。
文件的內建方法
open()成功執行并返回一個文件對象后,所有的后續操作通過句柄執行,方法分為四類:輸入,輸出,文件內移動,雜項類操作。
輸入
readline()方法讀取打開文件的一行(讀取下一個行結束符之前的所有字節)。然后整行,包括行結束符,作為字符串返回。和read()相同,他也有一個可選的size參數,默認為-1,代表讀至行結束符。如果提供了改參數,在超過size個字節后返回不完整的行。
readlines()方法并不像其他兩個輸入方法一樣返回一個字符串,它會讀取所有(剩余的)行然后把他們作為一個字符串列表返回。它的可選參數sizhint代表返回的最大字節大小。如果它大于0,那么返回的所有應該有sizhint字節(可能稍微大于這個數字,因為要湊齊緩沖區大小)
Python里有一個對象來高效地迭代文件的行:xreadlines對象。調用file.xreadlines()等價于xreadlines.xreadlines(file). xreadlines()對象不是一次性讀取所有的行,而是每次讀取一塊,所以在for循環時可以減少對內存的占用。不過隨著迭代器和文件迭代的引入,沒有必要再使用xreadlines()方法,因為iter(file)的效果也是一樣,或者在for循環中,用for eachLine in file代替它。它來得容易,去的也快。
輸出
write()內建方法功能與read()和readline()相反,它把含有文本數據或二進制數據塊的字符串寫入到文件中去。
和readlines()一樣,writelines()方法是針對列表的操作,它接受一個字符串列表作為參數,將它們寫入文件。行結束符并不會被自動加入,所以如果需要的話,你必須在調用writelines()前給每行結尾加上行結束符。
核心筆記:保留行分隔符
當使用輸入方法如read()或者readlines()從文件中讀取行時,Python并不會刪除行結束符,這個操作被留給了程序員。例如這樣的代碼在Python程序中很常見:
f = open('myFile','r')data = [line.strip() for line in f.readlines()]f.close()
類似地,輸出方法write() 或 writelines()也不會自動加入行結束符,應該在向文件寫入數據前自己完成。
文件內移動
seek()方法(類似C的fseek())可以再文件中移動文件指針到不同的位置。offset字節代表相對于某個位置偏移量。位置的默認值為0,代表文件從開頭算起,1代表從當前位置開始算起,2代表從文件末尾算起。
text()方法是對 seek()的補充,它告訴你當前文件指針在文件中的位置——從文件起始算起單位為字節。
文件的迭代
一行行訪問文件很簡單:
for eachLine in f:...
在這個循環里,eachLine代表文件的一行(包括末尾的行結束符),可以用來做任何想做的事情。
在以前的Python里,因為沒有引入迭代器,所以會有這樣的寫法:
for eachLine in f.readline():...
這是完成事情的老方法,現在可以不用readline()方法。
其它
close()通過關閉文件來結束對他的訪問,Python的垃圾收集機制也會在文件對象的引用計數降為零的時候自動關閉文件,良好的編程習慣要求在重新賦另個文件對象前關閉這個文件,如果不顯式關閉這個文件,那么可能丟失輸出緩沖區的數據。
fileno()方法返回打開文件的描述符,是一個整數,可以用在如os模塊( os.read())的一些底層操作。
flush()方法直接把內部緩沖區中的數據立刻寫入文件,而不是被動地等待輸出緩沖區被寫入。isatty()是一個布爾內建函數,當文件是一個類tty設備時返回True,否則返回False。truncate()方法將文件截取到當前文件指針位置或者到給定 size,以字節為單位。
文件方法雜項
在《Python核心編程》第二章里有這樣一個例子:
filename = raw_input('Enter file name:')f = open(filename, 'r')allLines = f.readlines()f.close()for eachline in allLines: PRint eachline, #加逗號是因為輸出有換行符
這樣一個方法是讀完所有的行再向屏幕輸出數據,如果文件太大,這樣的讀取方法并不好。所以用迭代器有這樣的方法:
filename = raw_input('Enter file name:')f = open(filename, 'r')for eachline in f: print eachline,f.close()
因為print默認在內容末尾加換行符,語句后加一個逗號可以避免換行。而readline()和readlines()不對行內的字符處理,拷貝時已經拷貝了換行符,所以不能再讓print自己換行,否則每行會有兩個換行符。
對于換行符,因為不同操作系統使用了不同的換行符,會對程序的編寫造成困擾,而Python考慮了這個問題
核心筆記:
行分隔符在不同操作系統里不同,POSIX(Unix或Mac OS X)上,換行符是/n ,而MacOS下用的是/r,DOS和Win32結合使用兩者。另外路徑分隔符在POSIX用"/"而DOS和Win用了"
/"
跨平臺應用很麻煩,而Python的os模塊設計者考慮到了這一點,os模塊有五個很有用的屬性
| os 模塊屬性 | 描述 |
| linesep | 用于在文件中分隔行的字符串 |
| sep | 用于分隔文件路徑名的字符串 |
| pathsep | 用于分隔文件路徑的字符串 |
| curdir | 當前工作目錄的字符串名稱 |
| pardir | 當前工作目錄的父目錄字符串名稱 |
文件對象還有一個truncate()方法,接受一個可選的size作為參數,如果給定,那么文件將被截取到最多size字節處。如果沒有size參數,那么默認截取到文件的當前位置。例如,剛打開一個文件,然后立即調用truncate()方法,那么你的文件(內容)實際上被刪除,這時候你是其實從0字節開始截?。╰ell()會返回這個數值)
下面這個例子是輸出到文件:
import osfilename = raw_input('Enter file name: ')fobj = open(filename, 'w')while True: aLine = raw_input("Enter a line('.'to quit):") if aLine != ".": fobj.write('%s%s' % (aLine, os.linesep)) else: breakfobj.close()
第二個例子是以可讀可寫模式:
>>> f = open(r'F:/tmp/x', 'w+')>>> f.tell()0>>> f.write('test line 1/n') # 加入一個長為12 的字符串 [0-11]>>> f.tell()13L>>> f.write('test line 2/n') # 加入一個長為12 的字符串 [12-23]>>> f.tell() # 告訴我們當前的位置26L>>> f.seek(-13, 1) # 向后移12 個字節>>> f.tell() # 到了第二行的開頭13L>>> f.readline()'test line 2/n'>>> f.seek(0, 0) # 回到最開始>>> f.readline()'test line 1/n'>>> f.tell() # 又回到了第二行13L>>> f.readline()'test line 2/n'>>> f.tell() # 又到了結尾26L>>> f.close() # 關閉文件
| 文件對象的方法 | 操作 |
| file.close() | 關閉文件 |
| file.fileno() | 返回文件的描述符(file descriptor ,FD, 整數值) |
| file.flush() | 刷新文件的內部緩沖區 |
| file.isatty() | 判斷 file 是否是一個類 tty 設備 |
| file.nexta() | 返回文件的下一行(類似于file.readline() ), 或在沒有其它行時引發 StopIteration 異常 |
| file.read(size=-1) | 從文件讀取 size 個字節, 當未給定 size 或給定負值的時候, 讀取剩余的所有字節, 然后作為字符串返回 |
| file.readintob(buf, size) | 從文件讀取 size 個字節到 buf 緩沖器(已不支持) |
| file.readline(size=-1) | 從文件中讀取并返回一行(包括行結束符), 或返回最大 size個字符 |
| file.readlines(sizhint=0) | 讀取文件的所有行并作為一個列表返回(包含所有的行結束符); 如果給定 sizhint 且大于 0 , 那么將返回總和大約為sizhint 字節的行(大小由緩沖器容量的下一個值決定) |
| file.xreadlinesc() | 用于迭代, 可以替換 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 前, 它只是字符串的列表 |
文件的內建屬性
文件除了方法外還有一些數據屬性。
| 文件對象的屬性 | 描述 |
| file.closed | True表示文件已經被關閉,否則為False |
| file.encoding | 文件使用的編碼——當Unicode字符串寫入數據時,它們將自動使用file.encoding 轉換為字節字符串;若file.encoding為None時使用系統默認編碼 |
| file.mode | 文件打開時使用的訪問方式 |
| file.name | 文件名 |
| file.newlines | 未讀取到行分隔符時為None,只有一種行分隔符時為一個字符串,當文件有多種類型的行結束符時,則為包含所有當前所遇到的行結束符的列表。 |
| file.softspace | 為0表示在輸出一數據后,要加上一個空格符,1表示不加。這個屬性一般只在內部使用 |
標準文件
一般說來,只要你的程序一執行,那么你就可以訪問三個標準文件。它們分別是標準輸入(一般是鍵盤),標準輸出(到顯示器的緩沖輸出)和標準錯誤(到屏幕的非緩沖輸出)。(這里所說的"緩沖"和"非緩沖"是指open()函數的第三個參數.)這些文件沿用的是C語言中的命名,分別為stdin,stdout和stderr。這三個文件在執行時都已經被預先打開了,只要知道文件句柄就可以隨時訪問這些文件。
Python中通過sys模塊訪問這些文件的句柄。導入sys模塊以后,就可以使用sys.stdin, sys.stdout 和 sys.stderr訪問。print 語句通常是輸出到 sys.stdout ;內建的raw_input()則從 sys.stdin接受輸入。
這些sys.*是文件,所以你必須自己處理好換行符,而print語句會自動在要輸出的字符串上加換行符。
命令行參數
sys模塊通過sys.argv屬性提供了對命令行參數的訪問。命令行參數是調用某個程序時除程序名以外的參數。
sys.argv是命令行參數的列表
len(sys.argv)是命令行參數的個數(也就是 argc)
命令行參數的意義是一個程序在執行完一些處理后,把結果作為流輸出出來,這些輸出的結果可以作為下一個程序輸入,然后這樣一次次延續下去,各個程序的輸出一般是不保存的,這樣可以節省大量的磁盤空間,各個程序的輸出通常使用“管道”實現到下個程序輸入的轉換。
Python有兩個模塊來輔助處理命令行參數。其中一個是getopt模塊,另一個是optparse。這里暫不詳解。
文件系統
對文件系統的訪問大多通過Python的os模塊實現。該模塊是Python訪問操作系統功能的主要接口。os模塊實際上只是真正加載的模塊的前端,真正的那個“模塊”明顯要依賴于具體的操作系統。有POSIX,NT,Mac,OS2等等。不需要導入這些模塊,只需要導入os模塊,Python會為我們選擇,這使得不需要考慮底層的工作。
除了對進程和進程運行環境進行管理外,os模塊還負責處理大部分的文件系統操作,應用程序開發人員可能要經常用到這些,這些功能包括刪除/重命名文件,遍歷目錄樹,以及管理文件訪問權限。
另一個模塊 os.path 可以完成一些針對路徑名的操作。它提供的函數可以完成管理和操作文件路徑名中的各個部分,獲取文件或各個部分,獲取文件或子目錄星系,文件路徑查詢等操作。
os 模塊的文件/目錄訪問函數
| 函數 | 描述 |
| 文件處理 | |
| mkfifo()/mknod() | 創建命名管道/創建文件系統節點 |
| remove()/unlink() | Delete file 刪除文件 |
| rename()/renames() | 重命名文件 |
| stat() | 返回文件信息 |
| symlink() | 創建符號鏈接 |
| utime() | 更新時間戳 |
| tmpfile() | 創建并打開('w+b')一個新的臨時文件 |
| walk() | 生成一個目錄樹下的所有文件名 |
| 目錄/文件夾 | |
| chdir()/fchdir() | 改變當前工作目錄/通過一個文件描述符改變當前工作目錄 |
| chroot() | 改變當前進程的根目錄 |
| listdir() | 列出指定目錄的文件 |
| gercwd()/getcwdu() | 返回當前工作目錄/功能相同,但返回一個Unicode對象 |
| mkdir()/makedirs() | 創建目錄/創建多層目錄 |
| rmdir()/removedirs() | 刪除目錄/刪除多層目錄 |
| 訪問/權限 | |
| assess() | 檢驗權限模式 |
| chmod() | 改變權限模式 |
| umask() | 設置默認權限模式 |
| 文件描述符操作 | |
| open() | 底層的操作系統 open (對于文件,使用標準內建 open()函數) |
| read()/write() | 根據文件描述符讀取/寫入數據 |
| dup()/dup2() | 復制文件描述符號/功能相同,但是復制到另一個文件描述符 |
| 設備號 | |
| makedev() | 從 major 和 minor 設備號創建一個原始設備號 |
| major()/minor() | 從原始設備號獲得 major/minor 設備號 |
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() | 兩個路徑名是否指向同個文件 |
以下有一個使用示例:
#!/usr/bin/env pythonimport osfor tmpdir in(r'/tmp',r'D:/temp'): if os.path.isdir(tmpdir): break else: print 'no temp directory availiable' tmpdir = ''if tmpdir: os.chdir(tmpdir) cwd = os.getcwd() print '*** current temporary directory' print cwd print '*** creating example directory...' os.mkdir('example') os.chdir('example') cwd = os.getcwd() print '*** new working directory: ' print cwd print '*** original directory listing: ' print os.listdir(cwd) print '*** creating test file...' fobj = open('test','w') fobj.write('foo/n') fobj.write('bar/n') fobj.close() print '*** updated directory listing: ' print os.listdir(cwd) print "*** renameing 'test' to 'filetest.txt'" os.rename('test','filetest.txt') print '*** updated directory listing: ' print os.listdir(cwd) path = os.path.join(cwd,os.listdir(cwd)[0]) print '*** full file pathname' print path print '***(pathname,basename==)' print os.path.split(path) print '***(filename,extenion)==' print os.path.splitext(os.path.basename(path)) print '*** diaplaying file contents:' fobj = open(path) for eachLine in fobj: print eachLine, fobj.close() print '*** deleting test file' os.remove(path) print '*** updated directory listing:' print os.listdir(cwd) os.chdir(os.pardir) print '*** deleting test drectory' os.rmdir('example') print '***DONE'
核心模塊: os (和 os.path)
從上面這些長篇討論可以看出,os 和 os.path 模塊提供了訪問計算機文件系統的不同方法。我們在本章學習的只是文件訪問方面,事實上 os 模塊可以完成更多工作。我們可以通過它管理進程環境,甚至可以讓一個Python程序直接與另外一個執行中的程序“對話”。
文件執行
無論你只是想簡單地運行一個操作系統命令,調用一個二進制可執行文件,或者其它類型的腳本(可能是 shell 腳本,Perl, 或是 Tcl/Tk),都需要涉及到運行系統其它位置的其它文件。盡管不經常出現,但有時甚至會需要啟動另外一個Python解釋器。之后再會學到這部分的內容
永久存儲模塊
永久存儲模塊可以把用戶的數據歸檔保存起來供以后使用,可以避免每次輸入同樣的信息。大部分永久性存儲模塊是用來儲存字符串數據的,但是也有方法來歸檔 Python 對象。
核心模塊: pickle 和 cPickle
可以使用 pickle 模塊把 Python 對象直接保存到文件里,而不需要把它們轉化成字符串,也不用底層的文件訪問操作把它們寫到一個二進制文件里。pickle 模塊會創建一個 Python 語言專用的二進制格式,不需要考慮細節,它會干凈利落地完成讀寫對象操作,唯一需要的只是一個合法的句柄。
pickle 模塊中的兩個主要函數是 dump() 和 load().
dump()函數接受一個文件句柄和一個數據對象作為參數,把數據對象以特定格式保存到給定文件里。當我們用load()函數從文件中取出已保存的對象時,pickle 知道如何恢復這些對象到它們本來的格式。
cPickle 是 pickle 的一個更快的 C 語言編譯版本
新聞熱點
疑難解答