任何程序都需要處理輸入和輸出。本章節(jié)介紹了處理各種不同類型文件時的慣用方法,包括文本和二進(jìn)制人間的處理七兜,文件的編碼以及其他的一些相關(guān)內(nèi)容。
1.讀寫文本數(shù)據(jù)
>>>with open ('somefile.txt','rt') as f:
>>> data = f.read()
>>>#Write chunks of text data
>>>with open('somefile.txt','wt') as f:
>>> f.write(text1)
>>> f.write(text2)
類似地福扬,要對文本文件執(zhí)行寫入操作腕铸,可以使用open()函數(shù)的wt模式來完成,如果待操作的文件已經(jīng)存在铛碑,那么這回清除并覆蓋其原先的內(nèi)容狠裹;
如果要在已存在的文件的結(jié)尾處追加內(nèi)容,可以使用open()函數(shù)的at模式汽烦;
- 一般來說涛菠,讀寫文本文件都是非常簡單直接的,但是撇吞,這里了還是有幾個微妙的細(xì)節(jié)需要引起注意俗冻;首先,我們在實(shí)例中采用了with語句牍颈,這會為使用的文件創(chuàng)建一個上下文環(huán)境迄薄,當(dāng)程序的控制流離開with語快后,文件將會自動關(guān)閉煮岁;我們并不一定要用with語句噪奄,但是如果我們不用的話請確保要記得手動關(guān)閉文件;
>>>f = open('somefile.txt','rt')
>>>data = f.read()
>>>f.close()
關(guān)于默認(rèn)換行符人乓,open()函數(shù)提供了一個newline=''的參數(shù)勤篮;
with open('somefile.txt','rt',newline = '') as f:
關(guān)于文本文件中可能出現(xiàn)的編碼錯誤,當(dāng)encoding = 'utf-8';
UnicodeDecodeError:'ascii' codec can't decode byte 0xc3 in position
如果遇到這樣的錯誤色罚,這通常表示沒有一正確的編碼方式來讀取文件碰缔。可以使用encoding來制定不同的編碼方式戳护;如果還不能避免的話金抡,則可以為open()函數(shù)提供一個可選的errors參數(shù)來慘厲錯誤瀑焦;
>>>#Replace bad chars with Unicode U+fffd replacement char
>>>f = open(somefile.txt','rt',encoding = 'ascii',errors = 'replace')
>>>f.read()
'Spicy Jalape?o'
>>>#Replace bad chars entirely
>>>f = open(somefile.txt','rt',encoding = 'ascii',errors = 'ignore')
>>>f.read()
'Spicy Jalapeo'
如果常常在擺弄open()函數(shù)的encoding和errors參數(shù),并為此做了大量的技巧性操作梗肝,那就適得其反了榛瓮;因?yàn)樯畋静粦?yīng)該如此艱難,關(guān)于文本巫击,第一條守則就是只需要確辟飨總是使用正確的文本編碼形式即可;請使用默認(rèn)的編碼設(shè)定(通常utf-8)
2.將輸出重定向到文件中
問題:我們想把print()函數(shù)的輸出重定向到一個文件中坝锰。
解決方案:我們只需要在print()函數(shù)加上file關(guān)鍵字參數(shù)即可粹懒;
>>>with open('somefile.txt','rt') as f:
>>> print("hello world!",f)
3.以不同的分隔符或行結(jié)尾完成打印
問題:我們想通過print()函數(shù)輸出數(shù)據(jù),但是同時也希望修改分隔符或者行結(jié)尾符
解決方案:可以在print()函數(shù)中使用sep和end關(guān)鍵字參數(shù)來根據(jù)需要修改輸出顷级;
>>> print('ACME',50,90.4)
ACME 50 90.4
>>> print('ACME',50,90.4,sep=',')
ACME,50,90.4
>>> print('ACME',50,90.4,sep=',',end='!!\n')
ACME,50,90.4!!
我們可以使用end參數(shù)在輸出中禁止打印出換行符的方式:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
>>> for i in range(5):
... print(i,end=' ')
...
0 1 2 3 4 >>>
str.join 問題也可以處理簡單的字符分隔文本問題凫乖;
>>> row = ('ACME',50,91.5)
>>> print(','.join(row))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 1: expected str instance, int found
>>> print(','.join(str(w) for w in row))
ACME,50,91.5
更加高效的方法:
>>> print(*row,sep = ',')
ACME,50,91.5
4.對鞋二進(jìn)制數(shù)據(jù)
解決方案:使用open()函數(shù)的rb模式或者wb模式就可以實(shí)現(xiàn)對二進(jìn)制數(shù)據(jù)的讀或者寫;
5.對已不存在的文件執(zhí)行寫入操作
問題:我們想將數(shù)據(jù)寫入到一個文件中弓颈,但只當(dāng)該文件已經(jīng)不在文件系統(tǒng)中時才這么做帽芽;
解決方案:這個問題可以通過使用open()函數(shù)中鮮為人知的x模式替換常見的w模式來解決
>>> with open('d:\\tmp.txt','wt') as f:
... f.write("Hello\n")
...
6
>>> with open('d:\\tmp.txt','xt') as f:
... f.write("Hello\n")
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'd:\\tmp.txt'
如果文件時二進(jìn)制模式,那么用xb替代xt即可翔冀;
6.在字符串上執(zhí)行I/O操作
7.讀寫壓縮的數(shù)據(jù)文件
問題:我們需要讀寫以gzip嚣镜、bz2格式壓縮的文件中的數(shù)據(jù):
解決方案:gzip、bz2,模塊使得同這類壓縮型文件打交道變得非常簡單橘蜜,這兩個模塊都提供了open()的其他實(shí)現(xiàn)菊匿,可用于處理壓縮文件;
#gzip compression
>>>import gzip
>>>with gzip.open('somefile.gz','rt') as f:
>>> text = f.read()
#bz2 compression
>>>import bz2
>>>with bz2.open('somefile.bz2','rt') as f:
>>> text = f.read()
對應(yīng)的寫入文件也相似计福;
大部分情況下讀寫壓縮數(shù)據(jù)都是簡單而直接的跌捆,單請注意,選擇正確的文件模式是至關(guān)重要的象颖。如果沒有指定的模式佩厚,那么默認(rèn)的模式是二進(jìn)制,這會使得期望接受文本的程序奔潰说订。gzip.open()和bz2.open()所接受的參數(shù)和內(nèi)建的open()函數(shù)一樣抄瓦,也支持encoding,newline陶冷,errors等關(guān)鍵字參數(shù)钙姊;
當(dāng)寫入壓縮數(shù)據(jù)時,壓縮級別可以通過compresslevel關(guān)鍵字參數(shù)來指定埂伦,這是可選的煞额;
>>>with gzip.open('somefile.gz','wt',compresslevel = 5) as f:
>>> f.write(text)
默認(rèn)級別是9,代表著最高的壓縮等級。低等級的壓縮可帶來更好的性能表現(xiàn)膊毁,但壓縮比就沒那么大胀莹;
8.對固定大小的記錄進(jìn)行迭代
9.將二進(jìn)制數(shù)據(jù)兌取到可變緩沖區(qū)中
10.對二進(jìn)制文件做內(nèi)存映射
11.處理路徑名
問題:我們需要處理路徑名以找出基文件名,目錄名婚温,絕對路徑等相關(guān)信息描焰;
解決方案:要操作路徑名,可以使用os.path模塊中的函數(shù)
>>> import os
>>> path = "D:\ostext\meizitu\mzt01\\10a33.jpg"
>>> os.path.basename(path)
'10a33.jpg'
>>> os.path.dirname(path)
'D:\\ostext\\meizitu\\mzt01'
>>> os.path.join('tmp','data',os.path.basename(path))
'tmp\\data\\10a33.jpg'
>>> os.path.expanduser(path)
'D:\\ostext\\meizitu\\mzt01\\10a33.jpg'
#分離擴(kuò)展名
>>> os.path.splitext(path)
('D:\\ostext\\meizitu\\mzt01\\10a33', '.jpg')
一栅螟、python中對文件荆秦、文件夾操作時經(jīng)常用到的os模塊和shutil模塊常用方法。
1.得到當(dāng)前工作目錄嵌巷,即當(dāng)前Python腳本工作的目錄路徑: os.getcwd()
2.返回指定目錄下的所有文件和目錄名:os.listdir()
3.函數(shù)用來刪除一個文件:os.remove()
4.刪除多個目錄:os.removedirs(r“c:\python”)
5.檢驗(yàn)給出的路徑是否是一個文件:os.path.isfile()
6.檢驗(yàn)給出的路徑是否是一個目錄:os.path.isdir()
7.判斷是否是絕對路徑:os.path.isabs()
8.檢驗(yàn)給出的路徑是否真地存:os.path.exists()
9.返回一個路徑的目錄名和文件名:os.path.split() eg os.path.split('/home/swaroop/byte/code/poem.txt') 結(jié)果:('/home/swaroop/byte/code', 'poem.txt')
10.分離擴(kuò)展名:os.path.splitext()
11.獲取路徑名:os.path.dirname()
12.獲取文件名:os.path.basename()
13.運(yùn)行shell命令: os.system()
14.讀取和設(shè)置環(huán)境變量:os.getenv() 與os.putenv()
15.給出當(dāng)前平臺使用的行終止符:os.linesep Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'
16.指示你正在使用的平臺:os.name 對于Windows室抽,它是'nt'搪哪,而對于Linux/Unix用戶,它是'posix'
17.重命名:os.rename(old坪圾, new)
18.創(chuàng)建多級目錄:os.makedirs(r“c:\python\test”)
19.創(chuàng)建單個目錄:os.mkdir(“test”)
20.獲取文件屬性:os.stat(file)
21.修改文件權(quán)限與時間戳:os.chmod(file)
22.終止當(dāng)前進(jìn)程:os.exit()
23.獲取文件大邢邸:os.path.getsize(filename)
glob是python自己帶的一個文件操作相關(guān)模塊,內(nèi)容也不多兽泄,用它可以查找符合自己目的的文件漓概,就類似于Windows下的文件搜索,而且也支持通配符病梢,,?,[]這三個通配符胃珍,代表0個或多個字符,?代表一個字符蜓陌,[]匹配指定范圍內(nèi)的字符觅彰,如[0-9]匹配數(shù)字。
它的主要方法就是glob,該方法返回所有匹配的文件路徑列表钮热,該方法需要一個參數(shù)用來指定匹配的路徑字符串(本字符串可以為絕對路徑也可以為相對路徑)填抬,比如:
#我這里就是獲得C盤下的所有txt文件
>>>import glob
>>>glob.glob(r'c:/*.txt')
#獲得指定目錄下的所有jpg文件
glob.glob(r'E:/pic/*/*.jpg')
iglob方法:
獲取一個可編歷對象, 使用它可以逐個獲取匹配的文件路徑名隧期。與glob.glob()的區(qū)別是:glob.glob同時獲取所有的匹配路徑飒责,而 glob.iglob一次只獲取一個匹配路徑。這有點(diǎn)類似于.NET中操作數(shù)據(jù)庫用到的DataSet與DataReader仆潮。下面是一個簡單的例子:
import glob
#父目錄中的.py文件
f = glob.iglob(r'../*.py')
12.檢測文件是否存在
>>>os.path.exists('/etc/passwd')
True
>>>os.path.exists('/etc/spam')
False
#
>>>os.path.isfile('/etc/passwd')
>>>os.path.isdir('/etc/passwd')
>>>os.path.islink('/etc/passwd')
>>>os.path.realpath('/etc/passwd')
#
>>> os.path.getsize(path)
100714
>>> os.path.getmtime(path)
1512957742.67442
>>> import time
>>> time.ctime(os.path.getmtime(path))
'Mon Dec 11 10:02:22 2017'
13.獲取目錄內(nèi)容的內(nèi)部
解決方案:可以使用os.listdir()函數(shù)來獲取目錄中的文件列表宏蛉;
>>>import os
>>>names = os.listdir(path)
字符串的startswith()和endswith()方法對于篩選目錄中的內(nèi)容也同樣有用;
至于文件名的匹配性置¢茉危可以使用glob或者fnmatch()模塊;
>>>import glob
>>>pyfiles = glob.glob('somedir/*.py')
#
>>>from fnmatch import fnmatch
>>>pyfiles = [name for name in os.listdir('somedir')]
>>> if fnmatch(name,'*.py')
14.繞過文件名編碼
15.打印無法解碼的文件
16.為已經(jīng)打開的文件添加或修改編碼方式
17.將字節(jié)寫入到文本文件
18.將已有的文件描述符包裝為文件對象
19.創(chuàng)建臨時文件和目錄
20.同串口進(jìn)行通信
21.序列化Python對象
問題:我們需要將Python對象序列化為字節(jié)流,這樣就可以保存到文件中辟灰,存儲到數(shù)據(jù)庫中或者通過網(wǎng)絡(luò)連接進(jìn)行傳輸个榕;
解決方案:序列化數(shù)據(jù)最常見的做法就是使用pickle模塊,其中:要將某個對象轉(zhuǎn)存儲到文件中芥喇,可以舒勇pickle.dump();如果要從字節(jié)流中創(chuàng)建出對象西采,可以使用peckle.load()或者pickle.loads()函數(shù);