Python學習筆記八1:文件

@[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ù)賦值為0False,I/O就是無緩存的禾锤。如果是1True私股,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. 隨機存取

??在本章中,我將文件都視為狼纬,只能按順序從頭到尾讀取羹呵。實際上,可在文件中移動疗琉,之訪問感興趣的部分(稱為隨機存取)冈欢。為此,可使用可使用文件對象的兩個方法:seektell盈简。
??方法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']

??方法writelinesreadlines相反:接受一個字符串列表(實際上沼沈,可以是任何序列或可迭代對象),并將這些字符都寫入到文件或流中币厕。請注意列另,寫入時不會添加換行符,因此你必須自行添加旦装。另外页衙,沒有方法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)上使用,這些不一致就可能導致異常举反。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懊直,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子照筑,更是在濱河造成了極大的恐慌吹截,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凝危,死亡現(xiàn)場離奇詭異,居然都是意外死亡晨逝,警方通過查閱死者的電腦和手機蛾默,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捉貌,“玉大人支鸡,你說我怎么就攤上這事冬念。” “怎么了牧挣?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵急前,是天一觀的道長。 經(jīng)常有香客問我瀑构,道長裆针,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任寺晌,我火速辦了婚禮世吨,結果婚禮上,老公的妹妹穿的比我還像新娘呻征。我一直安慰自己耘婚,他們只是感情好,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布陆赋。 她就那樣靜靜地躺著沐祷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攒岛。 梳的紋絲不亂的頭發(fā)上赖临,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音阵子,去河邊找鬼思杯。 笑死,一個胖子當著我的面吹牛挠进,可吹牛的內(nèi)容都是我干的色乾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼领突,長吁一口氣:“原來是場噩夢啊……” “哼暖璧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起君旦,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤澎办,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后金砍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體局蚀,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年恕稠,在試婚紗的時候發(fā)現(xiàn)自己被綠了琅绅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鹅巍,死狀恐怖千扶,靈堂內(nèi)的尸體忽然破棺而出料祠,到底是詐尸還是另有隱情,我是刑警寧澤澎羞,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布髓绽,位于F島的核電站,受9級特大地震影響妆绞,放射性物質(zhì)發(fā)生泄漏顺呕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一摆碉、第九天 我趴在偏房一處隱蔽的房頂上張望塘匣。 院中可真熱鬧,春花似錦巷帝、人聲如沸忌卤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驰徊。三九已至,卻和暖如春堕阔,著一層夾襖步出監(jiān)牢的瞬間棍厂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工超陆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牺弹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓时呀,卻偏偏與公主長得像张漂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谨娜,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一航攒、Python簡介和環(huán)境搭建以及pip的安裝 4課時實驗課主要內(nèi)容 【Python簡介】: Python 是一個...
    _小老虎_閱讀 5,744評論 0 10
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發(fā)此異常趴梢。 O...
    我想起個好名字閱讀 5,306評論 0 9
  • 寫在前面的話 代碼中的# > 表示的是輸出結果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 2,753評論 0 8
  • 心中的焦慮還是每一次的挑動下沸騰漠畜。而事實上這種焦慮根本無濟于事,而每一個追索的舉動也是虛假的坞靶,只是一個自我安慰罷...
    Fleruie_Me閱讀 240評論 0 2
  • “秦雪憔狞!你快回來!你爸出事了彰阴!” 當秦雪趕到醫(yī)院的時候躯喇,手術室的燈還在亮著,手術室門前坐著自己快要哭癱的母親硝枉,抱著...
    花月殘閱讀 250評論 0 1