注:本文檔是學習《Python核心編程(第二版)》時的整理。
1.文件對象
文件對象不僅可以用來訪問普通的磁盤文件,也可以訪問任何其他類型抽象層面上的"文件"。一旦設置了合適的"鈎子",就可以訪問具有文件類型接口的其它對象,就好像訪問的是普通的文件。
2.文件內建函數(open()和file())
open()和file()提供了初始化輸入/輸出(I/O)操作的通用接口,open()內建函數成功打開文件會返回一個文件對象,否則引發一個異常。當操作異常,會產生一個IOError異常。基本語法如下:
file_object = open(file_name, access_mode='r', buffering=-1)
file_name是包含要打開的文件名字的字符串,可以是相對路徑或者絕對路徑。
可選變量access_mode是一個字符串,代表文件的打開模式,'r'表示讀取,'w'表示寫入,'a'表示追加。另外,'U'代表通用換行符支持。
使用'r'或者'U'模式打開的文件必須是已經存在的;使用'w'模式打開的文件若已經存在則首先清空,然后(重新)創建;以'a'模式打開的文件是為追加數據做准備的,所有寫入的數據都追加到文件的末尾,即使seek到了其他的地方,如果文件不存在,則自動創建。
另外,C語言中fopen()支持的模式在python下也可以使用,如'+'代表讀和寫。
可選參數buffering用於指示訪問文件所采用的緩沖方式,其中0表示不緩沖,1表示只緩沖一行數據,任何其他大於1的值代表使用給定值作為緩沖區的大小。不提供參數或者給定負值代表使用系統默認的緩沖機制。
1 >>> fp = open('/etc/motd') #以讀方式打開 2 >>> fp = open('test', 'w') #以寫方式打開 3 >>> fp = open('data', 'r+') #以讀寫方式打開 4 Traceback (most recent call last): 5 File "<stdin>", line 1, in <module> 6 IOError: [Errno 2] No such file or directory: 'data' 7 >>> fp = open(r'test.txt', 'rb') #以二進制讀模式打開
2.1 工廠函數file()
open()和file()函數具有相同的功能,可以任意替換,任何使用open()的地方,都可以用file()替換它。
2.2 文件內建方法
open()成功執行返回一個文件對象,所有對該文件的后續操作都將通過這個"句柄"進行,文件方法可以分為四類:輸入、輸出、文件內移動及雜項操作。
2.3 讀出
file.read([size])方法用來直接讀取字節到字符串中,最多讀取給定數目個字節,如果沒有給定size參數(默認值為-1,)或者size值為負,文件將被讀取至尾。
1 >>> f = open('test.txt') 2 >>> f.read() 3 'hello\nworld\n'
file.readline([size])方法讀取打開文件的一行(讀取下個行結束符之前的所有字節)。然后整行,包括行結束符,作為字符串返回。size參數可選,默認為-1,代表讀至結束符。如果提供了該參數,那么超過size個字節后會返回不完整的行。
1 >>> f = open('test.txt') 2 >>> f.readline() 3 'hello\n' 4 >>> f.readline() 5 'world\n'
file.readlines([sizehint])方法讀取所有(剩余的)行然后把它們作為一個字符串列表返回,可選參數sizeint代表返回的最大字節大小,如果大於0,那么返回的所有行應該大於有sizeint字節(可能稍微大於這個數字,因為需要湊齊緩沖區大小)。
1 >>> f = open('test.txt') 2 >>> f.readlines() 3 ['hello\n', 'world\n']
注:read()或readlines()從文件中讀取行時,並不會刪除行結束符,這需要程序員來操作。常用的操作如下:
1 >>> f = open('myFile', 'r') 2 >>> data = [line.strip() for line in f.readlines()] 3 >>> f.close()
2.4 寫入
file.write(str)方法與read()方法相反,把含有文本數據或二進制數據塊的字符串寫入到文件中去。
file.writelines(sequence)方法是針對列表的操作,它接受一個字符串列表作為參數,將它們寫入到文件,行結束符並不會自動加入,如果需要的話,必須在調用writelines()前給每行結尾加上行結束符。
2.5 文件內移動
seek(offset[, whence])方法(類似於C中的seek()函數)可以在文件中移動文件指針到不同的位置。offset字節代表相對於某個位置偏移量,可選參數whence表示起始位置,默認值為0,代表從文件開頭算起(及絕對偏移量),1代表從當前位置算起,2代表從文件末尾算起。
text()方法是對seek()的補充,告訴你當前文件指針在文件中的位置—從文件起始算起,單位為字節。
1 >>> f = open('myFile', 'w+') 2 >>> f.writelines(['hello\n', 'world\n']) 3 >>> f.readlines() 4 [] 5 >>> f.seek(0,0) 6 >>> f.readlines() 7 ['hello\n', 'world\n'] 8 >>> f.close() 9 >>>
3. 文件迭代
一行一行的訪問文件:
1 for eachLine in f: 2 ;
在本次循環中,eachLine代表文本文件的一行(包括末尾的行結束符)。
4. 其他
file.close()方法通過關閉文件來結束對它的訪問。
file.fileno()方法返回打開的文件的描述符。是一個整型。
file.flush()方法會直接把內部緩沖區中的數據立刻寫入文件,而不是被動的等待輸出緩沖區被寫入。
file.isatty()是一個布爾內建函數,當文件是一個類tty設備時返回True,否則返回False。
file.truncate([size])方法將文件截取到當前文件指針位置或者到給定的size,以字節為單位。如果沒有傳遞size參數,那么默認將截取到文件的當前位置。例如:剛打開了一個文件,然后立即調用truncate()方法,那么文件(內容)實際上被刪除,這時候其實是從0字節開始截取的。
file.tell()方法返回當前的文件指針的位置。
5. 文件方法雜項
使用迭代器讀取一個文件:
1 >>> filename = raw_input('Enter file name: ') 2 >>> f = open(filename, 'r') 3 >>> for eachLine in f: 4 ... print eachLine, 5 >>> f.close()
寫入到文件:
1 import os 2 3 filename = raw_input('Enter file name: ') 4 fobj = open(filename, 'w') 5 while True: 6 aline = raw_input('Enter a line(\'.\' to quit): ') 7 if aline != '.': 8 fobj.write("%s%s" %(aline, os.linesep)) 9 else: 10 break 11 fobj.close()
以讀寫模式創建一個新的文件(可能是清空了一個現有的文件),再向文件寫入數據后,使用seek()方法在文件內部移動,使用tell()方法展示移動過程。
1 >>> f = open('/tmp/x', 'w+') 2 >>> f.tell() 3 0 4 >>> f.write('test line 1\n') 5 >>> f.tell() 6 12 7 >>> f.write('test line 2\n') 8 >>> f.tell() 9 24 10 >>> f.seek(-12, 1) 11 >>> f.tell() 12 12 13 >>> f.readline() 14 'test line 2\n' 15 >>> f.seek(0, 0) 16 >>> f.readline() 17 'test line 1\n' 18 >>> f.readline() 19 'test line 2\n' 20 >>> f.tell() 21 24 22 >>> f.close() 23 >>>
6. 行分隔符和其他文件系統的差異
操作系統間所支持的行分隔符不同。Linux系統中,行分隔符是換行符NEWLINE(\n)字符,Windows32下是(\r\n)。另外路徑分隔符也有所差異。
python的os模塊可以幫我們解決這些問題。
os模塊屬性 |
描述 |
linesep |
用於在文件中分隔行的字符串 |
sep |
用於分割文件路徑名的字符串 |
pathsep |
用於分割文件路徑的字符串 |
curdir |
當前工作目錄的字符串名字 |
pathdir |
(當前工作目錄的)父目錄字符串名字 |
7. 文件內建屬性
文件對象還包括一些數據屬性,這些屬性保存了文件對象相關的附加數據。例如文件名、文件的打開模式、文件是否已被關閉、以及一個標志變量,它可以決定使用print語句打印下一行前是否要加入一個空白字符。屬性見附錄。
8. 標准文件
標准文件指的是:標准輸入(一般指鍵盤)、標准輸出(到顯示器的緩沖輸出)、和標准錯誤(到屏幕的非緩沖輸出)。python中可以通過sys來訪問這些文件的句柄,導入sys模塊后,可以使用sys.stdin、sys.stdout和sys.stderr訪問。print語句通常是輸出到sys.stdout,raw_input()則從sys.stdin接收輸入。
1 >>> fobj = sys.stderr 2 >>> fobj.write('abc\n') 3 abc 4 >>> f = sys.stdout 5 >>> f.write('def\n') 6 def
9. 命令行參數
sys模塊通過sys.argv屬性提供了命令行參數的訪問。命令行參數是調用某個程序時除程序名以外的其他參數。
C語言中,argc和argv分別代表參數個數(argument count)和參數向量(argument vector)。argv變量代表一個從命令行上輸入的各個參數組成的字符串數組;argc變量代表輸入的參數個數。
python中,argc就是sys.argv列表的長度,而該列表的第一項sys.argv[0]永遠是程序的名字。
1 import sys 2 3 print 'you entered', len(sys.argv), 'arguments...' 4 print 'they are: ', str(sys.argv) 5 6 V:\python>C:/Python27/python.exe v:/python/argv.py 78 tales 89 hawk 7 you entered 5 arguments... 8 they are: ['v:/python/argv.py', '78', 'tales', '89', 'hawk']
11. 文件系統
對文件系統的訪問是大多通過python的os模塊實現。os模塊除了對進程和線程的運行環境進行管理外,還負責處理大部分的文件系統的操作,這些功能包括刪除/重命令文件、遍歷目錄樹、以及管理文件訪問權限等,詳見附錄。
另外,os.path可以完成一些針對路徑名的操作。它提供的函數可以完成管理和操作文件路徑名的各個部分,獲取文件或子目錄信息,文件路徑查詢等操作。詳見附錄。
11.1 舉例
(1)導入os模塊,然后檢查並確認’/tmp’是否是一個合法目錄,並切換到這個臨時目錄,之后使用getcwd()方法確認當前位置。
1 >>> import os 2 >>> os.path.isdir('/tmp') 3 True 4 >>> os.chdir('/tmp') 5 >>> cwd = os.getcwd() 6 >>> cwd 7 '/tmp'
(2)接下來在臨時目錄創建一個子目錄,然后用listdir()方法確認目錄為空。
1 >>> os.mkdir('example') 2 >>> os.chdir('example') 3 >>> cwd = os.getcwd() 4 >>> cwd 5 '/tmp/example' 6 >>> os.listdir(cwd) 7 []
(3)接下來創建一個有兩行內容的test文件,之后列出目錄確認文件被成功創建。
1 >>> fobj = open('test', 'w') 2 >>> fobj.write('foo\n') 3 >>> fobj.write('bar\n') 4 >>> fobj.close() 5 >>> os.listdir(cwd) 6 ['test']
(4)接下來替換’test’文件名為’filetest.txt’,再列出文件的路徑,判斷文件是否為文件或者目錄,獲取(dirname(),basename())元組,獲取(filename, extension)元組。
1 >>> os.rename('test', 'filetest.txt') 2 >>> os.listdir(cwd) 3 ['filetest.txt'] 4 >>> path = os.path.join(cwd, os.listdir(cwd)[0]) 5 >>> path 6 '/tmp/example/filetest.txt' 7 >>> os.path.isfile(path) 8 True 9 >>> os.path.isdir(path) 10 False 11 >>> os.path.split(path) 12 ('/tmp/example', 'filetest.txt') 13 >>> os.path.splitext(os.path.basename(path)) 14 ('filetest', '.txt')
(5)最后,顯示文件的內容,之后刪除之前創建的文件和目錄
1 >>> fobj = open(path) 2 >>> for eachLine in fobj: 3 ... print eachLine, 4 ... 5 foo 6 bar 7 >>> fobj.close() 8 >>> os.remove(path) 9 >>> os.listdir(cwd) 10 [] 11 >>> os.chdir(os.pardir) 12 >>> os.rmdir('example')
總結上面的步驟,完整的代碼如下:
1 #!/usr/bin/env python 2 3 import os 4 5 for tmpdir in ('/tmp', r'c:\temp'): 6 if os.path.isdir(tmpdir): 7 break 8 else: 9 print 'no temp directory avaliable' 10 tmpdir = '' 11 12 if tmpdir: 13 os.chdir(tmpdir) 14 cwd = os.getcwd() 15 print '*** current temporary directory' 16 print cwd 17 18 print '*** creating example directory' 19 os.mkdir('example') 20 os.chdir('example') 21 cwd = os.getcwd() 22 print '*** new working directory' 23 print cwd 24 print '*** original directory listing' 25 print os.listdir(cwd) 26 print '*** creating test file' 27 fobj = open('test', 'w') 28 fobj.write('foo\n') 29 fobj.write('bar\n') 30 fobj.close() 31 print '*** updated directory listing' 32 print os.listdir(cwd) 33 34 print "renaming 'test' to 'filetest.txt'" 35 os.rename('test', 'filetest.txt') 36 print '*** updated directory listing' 37 print os.listdir(cwd) 38 39 path = os.path.join(cwd, os.listdir(cwd)[0]) 40 print '*** full file pathname' 41 print path 42 print "*** (pathname, basename) == " 43 print os.path.split(path) 44 print '*** (filename, extension) == ' 45 print os.path.splitext(os.path.basename(path)) 46 47 print '*** displaying file contents:' 48 fobj = open(path) 49 for eachLine in fobj: 50 print eachLine, 51 fobj.close() 52 53 print '*** deleting test file' 54 os.remove(path) 55 print '*** updated directory listing:' 56 print os.listdir(cwd) 57 os.chdir(os.pardir) 58 print '*** deleting test directory' 59 os.rmdir('example') 60 print '*** DONE'
輸出結果:
1 [root@192 python_code]# python ospathex.py
2 *** current temporary directory
3 /tmp 4 *** creating example directory 5 *** new working directory 6 /tmp/example 7 *** original directory listing 8 [] 9 *** creating test file 10 *** updated directory listing 11 ['test'] 12 renaming 'test' to 'filetest.txt' 13 *** updated directory listing 14 ['filetest.txt'] 15 *** full file pathname 16 /tmp/example/filetest.txt 17 *** (pathname, basename) == 18 ('/tmp/example', 'filetest.txt') 19 *** (filename, extension) == 20 ('filetest', '.txt') 21 *** displaying file contents: 22 foo 23 bar 24 *** deleting test file 25 *** updated directory listing: 26 [] 27 *** deleting test directory 28 *** DONE
12. 永久存儲模塊
永久存儲解決的是用戶需要輸入大批數據且數據會多次重復使用的情況,可以把用戶的數據歸檔保存起來供以后調用,這樣可以避免每次輸入同樣的數據。
12.1 pickle和marshal模塊
python提供了許多可以實現最小化永久性存儲的模塊,其中的一組(pickle和marshal)可以用來轉換並存儲python對象。該過程將基本類型復雜的對象轉換為一個二進制數據集合,這樣就可以把數據集合保存起來或者通過網絡發送,然后再重新把數據集合恢復成原來的對象格式,這個過程被稱為數據的扁平化、數據的序列化或者數據的順序化。
pickle和marshal模塊可以對python對象進行轉換,但本身並沒有提供“永久性存儲”的功能,因為他們沒有為對象提供名稱空間,也沒有提供對永久性存儲對象的並發寫入訪問,它們只能存儲轉換python對象,為保存和傳輸提供方便。marshal和pickle模塊的區別在於marshal只能處理簡單的python對象(數字、序列、映射以及代碼對象),而pickle還可以處理遞歸對象。
12.2 DBM風格的模塊
*db*系列的模塊使用傳統的DBM格式寫入數據,Python提供了DMB的多種實現:dbhash/bsddb、dbm、gdbm和dumbdbm等。如果不確定,最好使用anydbm模塊,它會自動檢測系統上已安裝的DBM兼容模塊,並選擇“最好”的一個。
12.3 shelve模塊
shelve模塊使用anydbm模塊尋找合適的DBM模塊,然后使用cPickle來完成對存儲轉換過程。shelve模塊允許對數據庫文件進行並發的讀訪問,但不允許共享讀/寫訪問。下圖展示了存儲轉換與永久性存儲模塊之間的關系,以及為何shelve對象能夠成為兩者的最好的選擇。
附錄:
文件對象的訪問模式
文件模式 |
操作 |
r |
以讀方式打開 |
rU或U |
以讀方式打開,同時提供通用換行符支持 |
w |
以寫方式打開(必要時情況) |
a |
以追加模式打開(從EOF開始,必要時創建新文件) |
r+ |
以讀寫模式打開 |
w+ |
以讀寫模式打開(參見w) |
a+ |
以讀寫方式打開(參見a) |
rb |
以二進制讀模式打開 |
wb |
以二進制寫模式打開(參見w) |
ab |
以二進制追加模式打開(參加a) |
rb+ |
以二進制讀寫模式打開(參見r+) |
wb+ |
以二進制讀寫模式打開(參見w+) |
ab+ |
以二進制讀寫模式打開(參見a+) |
文件對象方法
文件對象方法 |
操作 |
file.close() |
關閉文件 |
flie.fileno() |
返回文件的描述符(file description, FD,整型) |
file.flush() |
刷新文件的內部緩沖區 |
file.isatty() |
判斷file是否是一個類tty設備 |
file.next() |
返回文件的下一行(類似於file.readline()),或在沒有其他行時引發StopIteration異常 |
file.read(size=-1) |
從文件讀取size個字節,當未給定size或給定負值的時候,讀取剩余的所有字節,然后作為字符串返回。 |
file.readlines(sizeint=0) |
讀取文件的所有行並作為一個列表返回(包括所有的行結束符);如果給定sizeint且大於0,那么將返回總和大於為sizeint字節的行(大小由緩沖器容量的下一個值決定,比如說緩沖區的大小為4K的倍數,如果sizeint為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應該是一個返回字符串的可迭代對象 |
文件對象屬性
文件對象的屬性 |
描述 |
file.closed |
表示文件已經關閉,否則為False |
file.encoding |
文件所使用的編碼—當Unicode字符串被寫入數據時,它們將自動使用file.encoding轉換為字節字符串,若file.encoding為None時使用系統默認編碼。 |
file.mode |
Access文件打開時使用的訪問模式 |
file.name |
文件名 |
file.newlines |
未讀取到行分隔符時為None,只有一個行分隔符時為一個字符串,當文件有多種類型的行結束符時,則為一個包含所有當前所遇到過的行結束符的列表 |
file.softspace |
為0表示在輸出一數據后,要加上一個空格符,1表示不加。程序員一般不使用這個屬性。 |
os模塊的文件/目錄訪問函數
函數 |
描述 |
文件處理 |
|
mkfifo()/mknod() |
創建命名管道/創建文件系統節點 |
remove()/unlink() |
刪除文件 |
rename()/renames() |
重命名文件 |
stat() |
返回文件信息 |
symlink() |
創建符號鏈接 |
utime() |
更新時間戳 |
tmpfile() |
創建並打開('w+b')一個新的臨時文件 |
walk() |
生成一個目錄樹下的所有文件名 |
目錄/文件夾 |
|
chdir()/fchdir() |
改變當前工作目錄/通過一個文件描述符改變當前工作目錄 |
chroot() |
改變當前進程的根目錄 |
listdir() |
列出指定目錄的文件 |
getcwd()/getcwdu() |
返回當前工作目錄/功能相同,當返回一個Unicode對象 |
mkdir()/makedirs() |
創建目錄/創建多層目錄 |
mkdir()/removedirs() |
刪除目錄/刪除多層目錄 |
訪問/權限 |
|
access() |
檢驗權限模式 |
chmod() |
改變權限模式 |
chown()/lchown() |
改變owner和group ID/功能相同,但不會跟蹤鏈接 |
umask() |
設置默認權限模式 |
文件描述符操作 |
|
open() |
底層的操作系統open(對於文件,使用標准的內建open()函數) |
read()/write() |
根據文件描述符讀取/寫入數據 |
dup()/dup2() |
復制文件描述符號/功能相同,但是是復制到另一個文件描述符 |
設備號 |
|
makedev() |
從major和mirror設備號創建一個原始設備號 |
major()/minor() |
從原始設備號獲取major()/mirror設備號 |
os.path模塊中的路徑名訪問函數
函數 |
描述 |
分隔 |
|
basename() |
去掉目錄路徑,返回文件名 |
dirname() |
去掉文件名,返回目錄路徑 |
join() |
將分離的各部分組合成一個路徑名 |
split() |
返回(dirname(),basename())元組 |
splitdrive() |
返回(drivename, pathname)元組 |
splittext() |
返回(filename, extension)元組 |
信息 |
|
getatime() |
返回最近訪問時間 |
getctime() |
返回文件創建時間 |
getmtime() |
返回最近文件修改時間 |
getsize() |
返回文件大小(以字節為單位) |
查詢 |
|
exists() |
指定路徑(文件或目錄)是否存在 |
isabs() |
指定路徑是否是絕對路徑 |
isdir() |
指定路徑是否存在且為一個目錄 |
isfile() |
指定路徑是否存在且為一個文件 |
islink() |
指定路徑是否存在且為一個符號鏈接 |
ismount() |
指定路徑是否存在且為一個掛載點 |
samefile() |
兩個路徑名是否指向同一個文件 |