@[toc]
??本章將進一步叛溢,讓程序能夠與更大的外部世界交互:文件和流
序:編碼
??gbk
漢字編碼 還有個名字叫cp936塑悼,漢字占2個字節(jié),windows默認的 中國用的
??utf-8
萬國碼 漢字占3字節(jié)楷掉, python,linux 默認
一厢蒜、打開文件
??要打開文件,可使用函數(shù)open
烹植,它位于自動導入模塊io
中斑鸦。函數(shù)open
將文件名作為唯一必不可少的參數(shù),并返回一個文件對象草雕。
open函數(shù)的基本語法如下:
open(file_name[,access_mode][,buffering])
-
fele_name變量
:是一個包含要訪問的文件名稱的字符串值巷屿。 -
access_mode變量
:指打開文件的模式 -
buffering
:如果buffering的值被設為0,就不會有寄存墩虹;如果buffering的值取1嘱巾,訪問文件時就會寄存行;如果將buffering的值設為大于1的整數(shù)诫钓,表示這就是寄存區(qū)的緩存旬昭;如果取負值,寄存區(qū)的緩沖大小就是系統(tǒng)默認的值尖坤。
??如果當前目錄中有一個名為somefile.txt
的文本文件稳懒,則可像下面這樣打開它:
>>> f = open('somefile.txt')
??默認創(chuàng)建在py文件所在的地方闲擦。
??如果文件位于其他地方慢味,可指定完整路徑。如果找不到文件墅冷,就會報錯纯路。
>>> f = open(r'C:\Users\JiaNeng\Desktop\20班視頻\文件操作及異常\2.txt')
# open函數(shù)返回一個File對象。
>>> print(f.name) #C:\Users\JiaNeng\Desktop\20班視頻\文件操作及異常\2.txt
這里有幾個概念要弄清楚寞忿。
- 文件路徑:文件的路徑是指文件在計算機上的位置驰唬。文件路徑又分為絕對路徑和相對路徑。
- 絕對路徑:總是從根文件夾開始。比如在Windows環(huán)境下叫编,一般從c盤辖佣、d盤等開始,c盤搓逾、d盤被稱為根文件夾卷谈,在該盤中的文件都得從根文件夾開始往下一級一級查找。在Linux環(huán)境下霞篡,一般從usr世蔗、home等根文件夾開始。比如在上面的示例程序中朗兵,path變量值就是一個絕對路徑污淋,在文件搜索框中輸入絕對路徑可以直接找到該文件。
- 相對路徑:相對于程序當前工作目錄的路徑余掖。比如當前工作文件存放的絕對路徑是
D:\python\workspace
寸爆,如果使用相對路徑,就可以不寫這個路徑盐欺,用一個.
號代替這個路徑值而昨。
>>> path = './test.txt'
??除了單個點,還可以使用兩個點表示父文件夾(或上一級文件夾)找田。
1. 文本模式
??如果要通過寫入文本來創(chuàng)建文件歌憨,這種調(diào)用函數(shù)open
的方式并不能滿足需求。為解決這種問題墩衙,可使用函數(shù)open
的第二個參數(shù)务嫡。
??調(diào)用函數(shù)open
時,如果只指定文件名漆改, 將獲得一個可讀取的文件對象心铃。如果要寫入文件,必須通過指定模式來顯式地指出這一點挫剑。函數(shù)open
的參數(shù)mode
的可能取值有多個去扣。
<center>表 函數(shù)open的參數(shù)mode的最常見取值 </center>
值 | 描述 |
---|---|
'r' | 讀取模式(默認值),文件不存在時會報錯 |
'w' | 寫入模式樊破,文件存在會清空之前的內(nèi)容愉棱,文件不存在則會新建文件 |
'x' | 獨占寫入模式,文件存在會報錯哲戚,文件不存在會新建文件 |
'a' | 附加模式奔滑,不清空之前的文件,直接將寫入的內(nèi)容添加到后面 |
'b' | 二進制模式(與其他模式結合使用) |
't' | 文本模式(默認值顺少,與其他模式結合使用) |
'+' | 讀寫模式(與其他模式結合使用) |
'r' | 顯示地指定讀取模式的效果與根本不指定模式相同朋其。 |
??'w'
寫入模式讓你能夠?qū)懭胛募踉。⒃谖募淮嬖跁r創(chuàng)建它。
??'x'
獨占地寫入模式更進一步梅猿,在文件已存在時引發(fā)FileExistsError異常
氓辣。在寫入模式下打開文件時,既有內(nèi)容將被刪除(截斷)袱蚓,并從文件開頭處開始寫入筛婉;如果要在既有文件末尾繼續(xù)寫入,可使用附加模式癞松。
'a'
打開一個文件用于追加爽撒。如果該文件已存在,文件指針就會放在文件的結尾响蓉。如果該文件不存在硕勿,就創(chuàng)建新文件進行寫入。
??當參數(shù)帶上'b'
時枫甲,表示可以用來讀取一個二進制文件源武。
??'+'
可與其他任何模式任何模式結合起來使用,表示既可讀取也可寫入想幻。例如粱栖,要打開一個文本文件進行讀寫,可使用'r+'
脏毯。請注意闹究,'r+'
和'w+'
之間有個重要差別:后者截斷文件,而前者不會這樣做食店。
??默認模式為'rt'
渣淤,這意味著將把文件視為經(jīng)過編碼的Unicode文本,因此將自動執(zhí)行解碼和編碼吉嫩,且默認使用UTF-8編碼价认。要指定其他編碼和Unicode錯誤處理策略,可使用關鍵字參數(shù)encodeing和errors自娩。這還將自動轉(zhuǎn)換換行字符用踩。默認情況下,行以'\n'結尾忙迁,讀取時將自動替換其他行尾字符('\r'或'\r\n')脐彩;寫入時將'\n'替換為系統(tǒng)的默認行尾字符。(os.linesep)动漾。
??通常丁屎,Python使用通用換行模式荠锭。在這種模式下旱眯,后面將討論的readlines等方法能夠識別所有合法的換行符(\n,\r和\r\n)如果要使用這種模式,同時禁止自動轉(zhuǎn)換,可將關鍵字參數(shù)newline
設置為空字符串删豺,如open(name, newline='')
共虑。如果要指定只將'\r'
或'\r\n'
視為合法的行尾字符,可將參數(shù)newline
設置為相應的行尾字符呀页。這樣妈拌,讀取時不會對行尾字符進行轉(zhuǎn)換,但寫入時將把'\n'
替換為指定的行尾字符蓬蝶。
??如果文件包含非文本的二進制數(shù)據(jù)尘分,如聲音剪輯片段或圖像,你肯定不希望執(zhí)行上述自動轉(zhuǎn)換丸氛。為此培愁,只需使用二進制模式(如'rb')來禁用與文本相關的功能。
2. 緩沖
??open函數(shù)的第3個參數(shù)是可選擇的缓窜,該參數(shù)控制文件的緩存定续。如果該參數(shù)賦值為0
或False
,I/O就是無緩存的禾锤。如果是1
或True
私股,I/O就是有緩存的。大于1的整數(shù)代表緩存的大小(單位是字節(jié))恩掷。-1
或小于0的整數(shù)
代表使用默認的緩存大小倡鲸。
??緩存一般指的是內(nèi)存,計算機從內(nèi)存中讀取數(shù)據(jù)的速度遠遠大于從磁盤讀取數(shù)據(jù)的速度黄娘,一般內(nèi)存大小遠小于磁盤大小旦签,內(nèi)存的速度比較快,但資源比較緊張寸宏,所以這里有是否對數(shù)據(jù)進行緩存的設置宁炫。
I/O在計算機中指Input/Output
,也就是輸入和輸出氮凝。由于程序和運行時數(shù)據(jù)在內(nèi)存中駐留羔巢,由CPU這個超快的計算核心執(zhí)行,涉及數(shù)據(jù)交換的地方通常是磁盤罩阵、網(wǎng)絡等竿秆,因此需要I/O接口。
??比如打開瀏覽器稿壁,訪問百度首頁幽钢,瀏覽器需要通過網(wǎng)絡I/O獲取百度網(wǎng)頁。瀏覽器首先會發(fā)送數(shù)據(jù)給百度服務器傅是,告訴它我想要首頁的HTML匪燕,這個動作是往外發(fā)數(shù)據(jù)蕾羊,叫Output
;隨后百度服務器把網(wǎng)頁發(fā)過來帽驯,這個動作是從外面接收數(shù)據(jù)龟再,叫Input
。通常尼变,程序完成I/O操作會有Input和Output兩個數(shù)據(jù)流利凑。
二、文件的基本方法
??本章介紹文件對象的一些基本方法以及其他類似于文件的對象(有時稱為流)嫌术。類似于文件的對象支持文件對象的一些方法哀澈,如支持read或write,或者兩者都支持度气。在開始之前日丹,首先需要了解一下流的概念。
??I/O編程中蚯嫌,流(Stresm)是一個很重要的概念哲虾。可以把流想像成一個水管择示,數(shù)據(jù)就是水管里的水束凑,但是只能單向流動。Input Stream就是數(shù)據(jù)從外面(磁盤栅盲、網(wǎng)絡)鎏金內(nèi)存汪诉,Output Stream就是數(shù)據(jù)從內(nèi)存流到外面去。瀏覽網(wǎng)頁時谈秫,瀏覽器和服務器之間至少需要建立兩根水管扒寄,才能既發(fā)送數(shù)據(jù)又接收數(shù)據(jù)。
1. 讀取和寫入
??文件最重要的功能就是提供和接收數(shù)據(jù)拟烫。如果有一個名為f的類似于文件的對象该编,可使用f.write
來寫入數(shù)據(jù),還可使用f.read
來讀取數(shù)據(jù)硕淑。與Python的其他大多數(shù)功能一樣课竣,在哪些東西可用作數(shù)據(jù)方面,也存在一定的靈活性置媳,但在文本和二進制模式下于樟,基本上分別將str和bytes類用作數(shù)據(jù)。
??每當調(diào)用f.write(string)
時拇囊,你提供的字符串都將寫入到文件中既有內(nèi)容的后面迂曲。
>>> f.open(somefile.txt', 'w')
>>> f.write('Hello, ') #7
>>> f.write('Word!') 6
>>> f.close()
??請注意:使用完文件后,我調(diào)用了方法close寥袭。
??讀取也一樣簡單路捧,只需告訴流你要讀取多少個字符(在二進制模式下是多少字節(jié))
>>> f.open(somefile.txt', 'r')
>>> f.read(4) #'Hell'
>>> f.read() #'o, World!'
??首先关霸,指定了要讀取4個字符。接下來鬓长,沒有指定讀取字節(jié)數(shù)時谒拴,read方法
會讀取打開文件中的所有字節(jié)尝江。把所有內(nèi)容都放在字符串里包括換行符\n
涉波。讀到最后一行,再讀就輸出空字符串炭序。
2. 使用管道重定向輸出
??在bash
等shell中啤覆,可依次輸入多個命令,并使用管道將它們鏈接起來惭聂。
$ cat somefile.txt | python somescript.py | sort
這條管道線包含三個命令窗声。
-
cat somefile.txt
:將文件somefile.txt的內(nèi)容寫入到標準輸出(sys.stdout) -
python somescript.py
:執(zhí)行Python腳本somescript。這個腳本從其標準輸入中讀取辜纲,并將結果寫入到標準輸出 -
sort
:讀取標準輸入(sys.stdin)中的所有文本笨觅,將各行按字母順序排序,并將結果寫入到標準輸出耕腾。
管道是將一個命令的標準輸出鏈接到下一個命令的標準輸入见剩。管道字符(|
)。因此扫俺,somscript.py從其sys.stdin中讀取數(shù)據(jù)苍苞,并將結果寫入到其sys.stdout
# somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print('Wordcount:', wordcount)
3. 隨機存取
??在本章中,我將文件都視為流狼纬,只能按順序從頭到尾讀取羹呵。實際上,可在文件中移動疗琉,之訪問感興趣的部分(稱為隨機存取)冈欢。為此,可使用可使用文件對象的兩個方法:seek
和tell
盈简。
??方法seek(offset, whence=0)
將當前位置(執(zhí)行讀取或?qū)懭氲奈恢?移到offset和whence指定的地方涛癌。
??參數(shù)offset
指定了字節(jié)(字符)數(shù),而參數(shù)whence
默認為0,這意味著偏移量是相對于文件開頭的(偏移量不能為負數(shù))。參數(shù)whence還可設置為1或2饲漾,其中前者表示相對于當前位置進行移動(偏移量可以為負)泊碑,而后者表示相對于文件末尾進行移動。返回值為新的位置弃衍。
??請看下面示例:
>>> f = open(r'C\text\somefile.txt','w')
>>> f.write('01234567890123456789') #20
>>> f.seek(5, 0) #5 指針從開頭偏移5
>>> f.write('Hello, World!') #13
>>> f.close()
>>> f = open(r'C\text\somefile.txt','w')
>>> f.read() #'01234Hello, World!89'
??方法tell()
返回當前位于文件的什么位置。
>>> f = open(r'C\text\somefile.txt','w')
>>> f.read(3) #'012'
>>> f.read(2) #'34'
>>> f.tell() #5
4. 讀取和寫入行
??與其逐個讀取流中的字符坚俗,不如成行地讀取镜盯。Python為我們提供了readline()
岸裙、readlines()
和writelines()
等方法用于行操作,例如:
path = './test.txt'
f_name = open(path,'w')
f_name.write('Hello,World!\n') #13
f_name = open(path,'a')
f_name.write('welcome!') #8
f_name = open(path,'r')
f_name.readline() # 'Hello,World!\n'
??要讀取一行(從當前位置到下一個分行符的文本)速缆,可使用方法readline
降允,換行符為\n
。
??調(diào)用這個方法時艺糜,可不提供任何參數(shù)(在這種情況下剧董,將讀取一行并返回它;也可提供一個非負整數(shù)破停,指定readline
最多可讀取多少個字符翅楼。readline方法
如果返回一個空字符串,說明已經(jīng)讀取到最后一行了真慢。
??如果將上面示例的最后一行改為:
f_name.readlines() #['Hello world!\n','welcome']
??要讀取文件中的所有行毅臊,并以列表的方式返回它們,可使用方法readlines
黑界。列表中的每個字符串就是文本中的每一行管嬉,并且換行符也會被輸出。
??readlines方法
可以傳入數(shù)值參數(shù)朗鸠,當傳入的數(shù)值小于等于列表中一個字符串的長度值時蚯撩,該字符串會被讀取童社;當傳入小于等于0的數(shù)值時求厕,所有字符都會被讀取。例如:
path = './test.txt'
f_name = open(path,'w')
str_list = ['Hello world!\n','welcome扰楼!\n', 'welcome呀癣!\n']
f_name.writelines(str_list)
f_name.flush() #刷新:將緩存中的內(nèi)容寫到文件中
# close() #close也有這種效果,但一般不這么用
f_name = open(path,'r')
f_name.read() # 'Hello world!\nwelcome弦赖!\nwelcome项栏!\n'
f_name = open(path,'r')
f_name.readlines() # ['Hello world!\n', 'welcome!\n', 'welcome蹬竖!\n']
??方法writelines
與readlines
相反:接受一個字符串列表(實際上沼沈,可以是任何序列或可迭代對象),并將這些字符都寫入到文件或流中币厕。請注意列另,寫入時不會添加換行符,因此你必須自行添加旦装。另外页衙,沒有方法writeline,因為可以使用wirte。
5. 關閉文件
??別忘了調(diào)用方法close
將文件關閉店乐。關閉文件沒有壞處艰躺,在有些操作系統(tǒng)和設置中,還可避免無意義地鎖定文件以防修改眨八。另外腺兴,這樣做還可避免用完系統(tǒng)可能指定的文件打開配額。
??對于寫入過的文件廉侧,一定要將其關閉页响,因為Python可能緩沖你寫入的數(shù)據(jù)(將數(shù)據(jù)暫時存儲在某個地方,以提高效率)伏穆。因此如果程序因某種原因崩潰拘泞,數(shù)據(jù)可能根本不會寫入到文件中纷纫。如果要重置緩沖枕扫,讓所做的修改反映到磁盤文件中,但又不想關閉文件辱魁,可使用方法flush
烟瞧。然而,需要注意的是染簇,根據(jù)所使用的操作系統(tǒng)和設置参滴,flush
可能處于鎖定考慮而禁止其他正在運行的程序訪問這個文件。
??在讀寫文件的過程中锻弓,出現(xiàn)異常的概率還是挺高的砾赔,特別對于大文件的讀取和寫入,出現(xiàn)異常更是家常便飯青灼。在讀或?qū)懳募倪^程中暴心,用try語句捕獲可能出現(xiàn)的異常。在捕獲異常前有一個動作要執(zhí)行杂拨,就是使用close方法
關閉文件专普。
??要確保文件得以關閉,可使用一條try/finally語句
弹沽,并在finally
子句中調(diào)用close
檀夹。
# 在這里打開文件
try:
# 將數(shù)據(jù)寫入到文件中
finally:
file.close()
??如果每次都要這么寫,就會很繁瑣策橘。實際上炸渡,有一條專門為此設計的語句,那就是with語句
丽已,它能自動幫我們調(diào)用close方法
蚌堵。
with open("somefile.txt") as somefile:
do_something(somefile)
??with語句
讓你能夠打開文件并將其賦給一個變量(這里是somefile)。在語句體中促脉,你將數(shù)據(jù)寫入文件(還可做其他事情)辰斋。到達該語句末尾時策州,將自動關閉文件,即便出現(xiàn)異常也是如此宫仗。
(1) 上下文管理器
??with語句
實際上是一個非常通用的結構够挂,允許你是用那個所謂的上下文管理器。上下文管理器是支持兩個方法的對象:__enter_
_和__exit__
藕夫。
-
方法__enter__
不接受任何參數(shù)孽糖,在進入with語句
時被調(diào)用,其返回值被賦給關鍵字as
后面的變量毅贮。 -
方法__exit__
接受三個參數(shù):異常類型办悟、異常對象和異常跟蹤。它在離開方法時被調(diào)用(通過前述參數(shù)將引發(fā)的異常提供給它)滩褥。如果__exit__
返回False病蛉,將抑制所有的異常。
??文件也可用作上下文管理器瑰煎。它們的方法__enter__
返回文件對象本身铺然,而方法__exit__
關閉文件。
6. 文件重命名
??在應用程序的過程中酒甸,我們可能需要程序幫助我們重命名某個文件的名字魄健,而不是通過手動的方式進行。
??Python的os模塊
為我們提供了rename方法
插勤,即文件重命名沽瘦。使用這個方法需要導入os模塊。??rename方法
的語法如下:
os.rename(current_file_name,new_file_name)
??該方法沒有返回值农尖。若文件不在當前目錄下析恋,則文件名要帶上絕對路徑。
open(./test1.txt/','w')
os.rename('test1.txt','test2.txt')
??若之前已經(jīng)創(chuàng)建了名為test1的文件卤橄,則將文件名更改為test2绿满;若之前沒有創(chuàng)建test1,則先創(chuàng)建test1窟扑,再更改名字喇颁。
7. 文件刪除
??Python的os模塊
為我們提供了remove方法
,即刪除文件嚎货。
??remove方法
的語法如下:
os.remove(file_name)
??若文件不在當前目錄下橘霎,則文件名要帶上絕對路徑。
??該方法沒有返回值殖属。
??該方法只能刪除已經(jīng)存在的文件姐叁,文件不存在就會拋異常。
8. 使用文件的基本方法
??假如文件somefile.txt包含以下文本,可對其執(zhí)行哪些操作呢外潜?
Welcome to this file
There is nothing here except
This stupid haiku
??首先是read(n)
>>> f = open(r'C:\Users\MIC\Desktop\somefile.txt')
>>> f.read(7) #'Welcome'
>>> f.read(4) #' to '
>>> f.close()
??接下來是read()
>>> f = open(r'C:\Users\MIC\Desktop\somefile.txt')
>>> print(f.read())
Welcome to this file
There is nothing here except
This stupid haiku
>>> f.close()
??下面是readline()
>>> f = open(r'C:\Users\MIC\Desktop\somefile.txt')
>>> for i in range(3):
print(str(i) + ':' + f.readline(), end='')
0:Welcome to this file
1:There is nothing here except
2:This stupid haiku
>>> f.close()
??最后是readlines()
>>> import pprint
>>> pprint.pprint(open(r'C:\Users\MIC\Desktop\somefile.txt').readlines())
['Welcome to this file\n',
'There is nothing here except\n',
'This stupid haiku\n']
>>> f.close()
??下面來嘗試寫入原环,首先是write(string)
>>> f = open(r'C:\Users\MIC\Desktop\somefile.txt','w')
>>> f.write('this \nis no\nhaiku') #17
>>> f.close()
??修改后的文本文件
this
is no
haiku
??最后是writelines(list)
>>> f = open(r'C:\Users\MIC\Desktop\somefile.txt')
>>> lines = f.readlines()
>>> f.close()
>>> lines[1] = "isn't a\n"
>>> f=open(r'C:\Users\MIC\Desktop\somefile.txt','w')
>>> f.writelines(lines)
>>> f.close()
??再次修改后的文本文件
this
isn't a
haiku
三、迭代文件內(nèi)容
??所謂迭代处窥,是指不斷重復一個動作嘱吗,直到這些動作都完成為止。
??一種常見的文件操作是迭代其內(nèi)容滔驾,并在迭代過程中反復采取某種措施谒麦。
??在本節(jié)的所有示例中,我都將使用一個名為process
的虛構函數(shù)來表示對每個字符串或行所做的處理哆致,你可以用自己的喜歡的方式實現(xiàn)這個函數(shù)绕德。其中,filename
也是虛構的摊阀。
??更有用的實現(xiàn)包括將數(shù)據(jù)存儲在數(shù)據(jù)結構中耻蛇、計算總和、使用模塊re
替換以及添加行號驹溃。
1.每次一個字符(或字節(jié))
??一種最簡單的文件內(nèi)容迭代是在while循環(huán)中使用方法read
城丧。例如延曙,你可能想遍歷文件中的每個字符(在二進制模式下是每個字節(jié))豌鹤,為此可像下面這樣做:
with open(filename) as f:
while True:
char = f.read(1)
if not char:break
process(char)
??如果你每次讀取多個字符(字節(jié)),可指定要讀取的字符(字節(jié))數(shù)枝缔。
??該示例對寫入文件的每個字符都進行循環(huán)了布疙。這個程序運行到文件末尾時,read方法
會返回一個空字符串愿卸,未執(zhí)行到空字符串前灵临,返回的都是非空字符,表示布爾值為真趴荸。
2. 每次一行
??如果處理文本文件時儒溉,你通常想做的是迭代其中的行,而不是每個字符发钝。通過使用readline
顿涣,可像迭代字符一樣輕松地迭代行。
with open(filename) as f:
while True:
line = f.readline()
if not line: break
process(line)
3. 讀取所有內(nèi)容
??如果文件不太大酝豪,可一次讀取整個文件涛碑;為此,可使用方法read
并不提供任何參數(shù)(將整個文件讀取到一個字符串中)孵淘,也可使用方法readlines
(將文件讀取到一個字符串列表中蒲障,其中每個字符串都是一行)。
??請注意:除進行迭代外,像這樣將文件內(nèi)容讀取到字符串或列表中也對完成其他任務很有幫助揉阎。
??例如庄撮,可對字符串應用正則表達式,還可將列表存儲到某種數(shù)據(jù)結構中供以后使用毙籽。
??迭代字符
with open(filename) as f:
for char in f.read():
process(char)
??迭代行
with open(filename) as f:
for line in f.readlines():
process(line)
4. 使用fileinput實現(xiàn)延遲行迭代
??我們前面介紹過read方法
和readlines方法
重窟,這兩個方法不帶參數(shù)時將讀取文件中所有內(nèi)容,然后加載到內(nèi)存中惧财。當需要迭代大型文本中的行巡扇,此時使用這種方式將占用太多內(nèi)存,甚至直接使內(nèi)存溢出垮衷,從而導致執(zhí)行失敗厅翔。
??當然,你可轉(zhuǎn)而結合使用while循環(huán)
和readline
搀突,但在Python中刀闷,在可能的情況下,應該首選for循環(huán)仰迁,使用for循環(huán)意味著可以對任務進行分隔操作甸昏,而不是一步到位。
??按行讀取文件時徐许,若能使用for循環(huán)施蜜,則稱之為懶加載式迭代。你可使用一種名為延遲行迭代的方法—說它延遲是因為它只讀取實際需要的文本部分雌隅。
??請注意:模塊fileinput
會負責打開文件翻默,你只需給它提供一個文件名即可。這些操作被封裝在input方法
內(nèi)部了恰起。
import fileinput
for line in fileinput.input(filename):
process(line)
fileinput.input()它幫助迭代多個輸入流中的行修械,它返回一個可在for循環(huán)中進行迭代的對象。
5. 文件迭代器
??文件實際上是可迭代的检盼,這意味著可在for循環(huán)
中直接使用它們來迭代行肯污。
with open(filename) as f:
for line in f:
process(line)
??在這些迭代示例中,我都將文件用作了上下文管理器吨枉,以確保文件得以關閉蹦渣。雖然這通常是個不錯的主意,但只要不寫入文件东羹,就并非一定要這樣做剂桥。如果你愿意讓Python去負責關閉文件,可進一步簡化這個示例属提,如下所示权逗。
for line in open(filename):
process(line)
??在這里美尸,我沒有將打開的文件賦給變量,因此沒法顯示地關閉它斟薇。
??請注意:sys.stdin
也是可迭代的师坎。因此要迭代標準輸入中的所有行,可像下面這樣做:
import sys:
for line in sys.stdin:
process(line)
??另外堪滨,可對迭代器做的事情基本上都可對文件做胯陋,如(使用list(open(filename))
)將其轉(zhuǎn)換為字符串列表,其效果與使用readlines
相同袱箱。
>>> f = open('somefile.txt','w')
>>> print('First', 'line', file = f)
>>> print('Second', 'line', file = f)
>>> print('Third', 'and final', file = f)
>>> f.close()
>>> lines = list(open('somefile.txt'))
>>> lines # ['First line\n', 'Second line\n', 'Third and final\n']
>>> first,second,third = open(r'C:\Users\MIC\Desktop\somefile.txt')
>>> first #'First line\n'
>>> second #'Second line\n'
>>> third #'Third and final\n'
??文件內(nèi)容
First line
Second line
Third and final
??print('a',file=f)
將文本輸入到file-like
對象中遏乔,可以是文件,數(shù)據(jù)流等等发笔,默認是sys.stdout
在這個示例中盟萨,需要注意如下幾點。
- 使用了print類似寫入文件了讨,這將自動在提供的字符串后面添加換行符捻激。
- 對打開的文件進行序列解包,從而將每行存儲到不同的變量中前计。(這種做法不常見胞谭,因為通常不知道文件包含多少行,但這演示了文件對象是可迭代的)
- 寫入文件后將其關閉男杈,以確保數(shù)據(jù)得以寫入磁盤丈屹。(如你所見,讀取文件后并沒有將其關閉势就。這可能有點粗糙泉瞻,但并非致命的。)
四苞冯、StringIO函數(shù)
??數(shù)據(jù)的讀取除了通過文件,還可以在內(nèi)存中進行侧巨。Python中的io模塊
提供了隊str操作的StringIO函數(shù)
舅锄。
??要把str寫入StringIO,我們需要創(chuàng)建一個StringIO
司忱,然后像文件一樣寫入皇忿。操作示例如下:
from io import StringIO
io_val = StringIO()
io_val.write('hello')
print('say:',io_val.getvalue())
# say: hello
??getvalue()
方法用于獲得寫入后的str。
??要讀取StringIO
坦仍,還可以用str
初始化StringIO
鳍烁,然后像文件一樣讀取。
from io import StringIO
io_val = StringIO('Hello\nWorld!\nWelcome!')
while True;
line = io_val.readline()
if line =='':
break
print('line value:',line.strip())
# line value: Hello
# line value: World!
# line value: Welcome!
五繁扎、序列化與反序列化
??在運行程序的過程中幔荒,所有變量都在內(nèi)存中糊闽,我們把變量從內(nèi)存中變成可存儲或傳輸?shù)倪^程稱為序列化。我們可以把序列化后的內(nèi)容寫入磁盤爹梁,或者通過網(wǎng)絡傳輸?shù)絼e的機器上右犹。反過來,把變量內(nèi)容從序列化的對象重新讀到內(nèi)存里稱為反序列化姚垃。
- 序列化是指將數(shù)據(jù)結構或?qū)ο筠D(zhuǎn)換成二進制串的過程念链。
- 反序列化是指將序列化過程中生成的二進制串轉(zhuǎn)換成數(shù)據(jù)結構或?qū)ο蟮倪^程。
下面我們介紹Python中序列化和反序列化的方式积糯。
1. 一般序列化與反序列化
??Python中的pickle模塊
實現(xiàn)了基本數(shù)據(jù)序列和反序列化掂墓。
- 通過pickle模塊的序列化操作,能夠?qū)⒊绦蛑羞\行的對象信息保存到文件中看成,從而永久存儲梆暮。
- 通過pickle模塊的反序列化操作,能夠從文件中創(chuàng)建上一次程序保存的對象绍昂。
??pickle模塊
的基本接口如下:
pickle.dump(obf,file,[,protocol])
??例如:
import pickle
d = dict(name='xiao zhi',num = 1002)
print(pickle.dumps(d))
#b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x08\x00\x00\x00xiao zhiq\x02X\x03\x00\x00\x00numq\x03M\xea\x03u.'
pickle,dumps()方法把任意對象序列化成一個bytes啦粹,然后把這個bytes寫入文件。也可以使用另一種方法pickle.dumps()窘游,直接把對象序列化后寫入一個文件中唠椭,程序如下:
try:
f_name = open('dump.txt','wb')
pickle.dump(d,f_name)
finally:
f_name.close()
??打開dump.txt文件,可以看到里面是一堆看不懂的內(nèi)容忍饰,這些都是python保存的對象內(nèi)部信息贪嫂。
??既然已經(jīng)將內(nèi)容序列化到文件中了,使用文件時就需要把對象從磁盤讀到內(nèi)存艾蓝×Τ纾可以先把內(nèi)容讀到一個bytes,然后用pickle.loads()方法
反序列化對象赢织;也可以直接用pickle.load()方法
從一個對象中直接反序列化對象亮靴。從dump.txt文件中將序列化的內(nèi)容反序列化的代碼如下:
import pickle
try:
f_name = open('dump.txt','rb')
print('load result:',pickle.load(f_name))
finally:
f_name.close()
執(zhí)行結果如下:
load result: {'num':1002, 'name':'xiao zhi'}
??由執(zhí)行結果看到,變量的內(nèi)容被正確讀取出來了于置。不過茧吊,雖然內(nèi)容相同,但是對應的變量已經(jīng)完全不同了八毯。
??注意:pickle的序列化和反序列化只能用于Python搓侄,不同版本的python可能彼此都不兼容,因此pickle一般用于保存不重要的數(shù)據(jù)话速,也就是不能成功反序列化也沒關系的數(shù)據(jù)讶踪。
2. JSON序列化與反序列化
??本節(jié)介紹的JSON方式是通用的。
??JSON是一種輕量級的數(shù)據(jù)交換格式泊交,是基于ECMAScript的一個子集乳讥。
??Python3中可以使用json模塊
對JSON數(shù)據(jù)進行編碼解碼柱查,包含以下兩個函數(shù)。
- json.dumps():對數(shù)據(jù)進行編碼
- json.loads():對數(shù)據(jù)進行解碼
??在JSON的編碼解碼過程中雏婶,Python的原始類型與JSON類型會相互轉(zhuǎn)換物赶,具體的轉(zhuǎn)化對照如表所示;
<center>Python編碼為JSON類型</center>
Python | JSON |
---|---|
dict | {} |
list,tuple | [] |
str | string |
int or float | number |
True/False | true/false |
None | null |
<center>JSON解碼為Python類型</center>
JSON | Python | Python | JSON |
---|---|---|---|
{} | dict | dict | {} |
[] | list | list,tuple | [] |
string | str | str | string |
number | int or float | int or float | number |
true/false | True/False | True/False | true/false |
null | None | None | null |
??下面是JSON序列化與反序列化的示例:
import json
data = {'num':1002,'name':'xiao zhi'}
json_str = json.dumps(data)
print("Python 原始數(shù)據(jù):", data)
print("JSON 對象:", json_str)
??執(zhí)行結果如下:
{'num':1002,'name':'xiao zhi'} # Python 原始數(shù)據(jù):
{'num':1002,'name':'xiao zhi'} # JSON 對象:
??接著以上示例,我么可以將一個JSON編碼的字符串轉(zhuǎn)換為一個Python數(shù)據(jù)結構留晚,代碼如下:
>>> data2 = jason.loads(json_str)
>>> data2 = json.loads(json_str)
>>> print("data2['name']:",data2['name']) #data2['name']: xiao zhi
>>> print("data2['num']:",data2['num']) #data2['num']: 1002
??如果要處理的是文件而不是字符串酵紫,就可以使用json.dump()和json.load()編碼、解碼JSON數(shù)據(jù)错维。
# 寫入JSON數(shù)據(jù)
with open('dump.txt','w') as f:
json.dump(data,f)
# 讀取數(shù)據(jù)
with OPen('dump.txt.','r') as f:
data = json.load(f)
六奖地、調(diào)試
??當我們讀取和寫入文件時,經(jīng)常遇到和空白字符相關的問題赋焕。這些問題可能很難調(diào)試参歹,因為空格、制表符和換行符通常是不可見的隆判,例如:
>>> str_val = '1 2\t 3\n 4 5'
>>> print(str_val)
1 2 3
4 5
??在這種情況下犬庇,Python為我們提供了repr函數(shù)
。該函數(shù)可接收任何對象作為參數(shù)侨嘀,并返回對象的字符串表達形式臭挽。
>>> print(repr(str_val))
# '1 2\t 3\n 4 5'
??結果把字符原本輸出了。在實際應用中咬腕,使用這種方式可以幫助調(diào)試欢峰。
??另一個經(jīng)常遇到的問題是不同系統(tǒng)使用不同的字符表示換行。有的系統(tǒng)使用換行符\n
表示換行涨共,有的系統(tǒng)使用回車符\r
表示換行纽帖。如果我們編寫的代碼在不同系統(tǒng)上使用,這些不一致就可能導致異常举反。