Python?IO文件管理的具體使用
文件操作
我們可以使用python來(lái)操作文件,比如讀取文件內(nèi)容、寫入新的內(nèi)容等,因?yàn)槿魏斡?jì)算機(jī)文件的本質(zhì)都是一些有不同后綴的字符組成的。
python文件操作的兩種模式
打開(kāi)模式
- while,寫入模式,簡(jiǎn)寫為 w ,指定的文件不存在則創(chuàng)建文件,存在則打開(kāi)并清空內(nèi)容,并且將文件指針(光標(biāo))放在文件的開(kāi)頭。
- read,讀取模式,簡(jiǎn)寫為 r ,文件不存在則報(bào)錯(cuò),存在則打開(kāi)文件,并且將文件指針?lè)旁谖募拈_(kāi)頭。
- append,追加模式,簡(jiǎn)寫為 a ,文件不存在則創(chuàng)建文件,存在則打開(kāi)文件,并且將指針?lè)旁谖募┪病?/li>
- xor,異或模式,簡(jiǎn)寫為 x ,文件存在則報(bào)錯(cuò),不存在則創(chuàng)建文件,將文件指針?lè)旁谖募拈_(kāi)頭。
擴(kuò)展模式
擴(kuò)展模式是用來(lái)配合打開(kāi)模式的輔助模式,擴(kuò)展模式單獨(dú)不能使用。
- plus,增強(qiáng)模式,簡(jiǎn)寫為 + ,可以讓打開(kāi)模同時(shí)具有讀寫功能。
- bytes,bytes模式,簡(jiǎn)寫為 b ,將文件按照二進(jìn)制字節(jié)流編碼進(jìn)行讀寫。
因此我們根據(jù)這兩種大的模式可以組合成為16種操作文件的方法。
模式 | 作用 | 模式 | 作用 |
---|---|---|---|
w | 寫入模式,只可寫,不可讀。 | a | 追加模式,只可寫,不可讀。 |
w+ | 寫入模式,可寫可讀。 | a+ | 追加模式,可寫可讀。 |
wb | 寫入模式,按照二進(jìn)制字節(jié)流編碼可寫不可讀 | ab | 追加模式,按照二進(jìn)制字節(jié)流可寫不可讀 |
wb+ | 寫入模式,按照二進(jìn)制字節(jié)流編碼可寫可讀 | ab+ | 追加模式,按照二進(jìn)制字節(jié)流可寫可讀。 |
r | 讀取模式,只可讀,不可寫。(默認(rèn)模式) | x | 異或模式,只可寫,不可讀。 |
r+ | 讀取模式,可寫可讀。 | x+ | 異或模式,可寫可讀。 |
rb | 讀取模式,按照二進(jìn)制字節(jié)流編碼可讀不可寫。 | xb | 異或模式,二進(jìn)制字節(jié)流可寫不可讀。 |
rb+ | 讀取模式,按照二進(jìn)制字節(jié)流編碼可讀可寫。 | xb+ | 異或模式,二進(jìn)制字節(jié)流可寫可讀。 |
異或模式和寫入模式的區(qū)別在于,異或模式如果打開(kāi)的文件在指定的路徑中如果存在,就會(huì)報(bào)錯(cuò);而寫入模式是直接打開(kāi)不會(huì)報(bào)錯(cuò),但是會(huì)將源文件中的所有內(nèi)容清空。因?yàn)閷懭肽J胶妥x取模式之間的互相配合,異或模式的使用頻率越來(lái)越少,正在逐步淘汰當(dāng)中。
編碼格式的了解
編碼是信息從一種形式或格式轉(zhuǎn)換為另一種形式的過(guò)程,就是用預(yù)先規(guī)定的方法將文字、數(shù)字或其它對(duì)象編成數(shù)碼,或?qū)⑿畔ⅰ?shù)據(jù)轉(zhuǎn)換成規(guī)定的電脈沖信號(hào)。這樣做的目的是為了簡(jiǎn)化信息之間的傳遞。但是為保證編碼的正確性,編碼要規(guī)范化、標(biāo)準(zhǔn)化,即需有標(biāo)準(zhǔn)的編碼格式。常見(jiàn)的編碼格式有ASCII、ANSI、GBK、GB2312、Unicode、UTF-8等。
所有的編碼格式,都是將字符轉(zhuǎn)換成對(duì)應(yīng)的二進(jìn)制格式。將西方的字母文字和數(shù)字按照一個(gè)字節(jié)的方式存儲(chǔ),而將亞洲中中、日、朝等文字按照多字節(jié)存儲(chǔ)。這是因?yàn)槲鞣降淖帜刚Z(yǔ)言,字母的數(shù)量遠(yuǎn)少于東方的文字?jǐn)?shù)量,因此編程工作中一般更加的傾向與盡量多的使用英文的原因,因?yàn)橄鄬?duì)的來(lái)說(shuō)使用漢字等字符較少的程序可以占據(jù)更少的系統(tǒng)資源。
常用的編碼格式英文原始編碼:ASCII碼
ACSII編碼只有128個(gè)字符,26個(gè)英文字母的大小寫之外,還有一些常用的符號(hào),還有一些不可或缺的系統(tǒng)控制字符等。ACSII編碼中沒(méi)有除了英文字母之外的其它語(yǔ)言字符。
中文國(guó)家標(biāo)準(zhǔn)編碼:GB系列編碼
凡是由GB開(kāi)頭的編碼集都是屬于中國(guó)國(guó)家的標(biāo)準(zhǔn)編碼字符集,只是不同的版本而已,使用這個(gè)編碼的漢字占用的系統(tǒng)資源最少,中文使用2個(gè)字節(jié)的存儲(chǔ)空間。比如GB2312。
萬(wàn)國(guó)碼:Unicode編碼
Unicode編碼包含世界上所有的文字,無(wú)論什么字符都以4個(gè)字節(jié)進(jìn)行存儲(chǔ)。這是Unicode編碼的缺點(diǎn),雖然擁有世界上最齊全的字符,但是占用的系統(tǒng)資源很大,所以很少使用。
因此在這個(gè)基礎(chǔ)之上改進(jìn),創(chuàng)建了可變長(zhǎng)的Unicode編碼集,UTF系列。這是目前世界上最主流的編碼字符集,在這個(gè)編碼集當(dāng)中,不用擔(dān)心任何字符會(huì)亂碼,字母文字和數(shù)字使用一個(gè)字節(jié)的存儲(chǔ)空間,中文等字符使用三個(gè)字節(jié)的存儲(chǔ)空間,大大節(jié)省了空間的占用。比如UTF-8。
open函數(shù)的使用
python中操作文件要使用到open函數(shù),open函數(shù)的作用是用于打開(kāi)一個(gè)文件,創(chuàng)建一個(gè)file對(duì)象,使用相關(guān)的方法調(diào)用它對(duì)文件進(jìn)行讀寫操作。
語(yǔ)法:open(file, mode=None, encoding=None)
參數(shù)說(shuō)明:
- file:文件的位置和名稱
- mode:操作的模式,使用簡(jiǎn)寫,就是我們上述的16中操作方式
- encoding:指定編碼類型,比如UTF-8、GB2312、ACSII等
open函數(shù)指定這些信息之后,返回一個(gè)TextIOWrapper對(duì)象,使用這個(gè)對(duì)象,我們可以按照指定的操作模式和編碼格式來(lái)操作我們指定的文件。
文件的寫入(寫入模式)
現(xiàn)在我們?cè)谑褂胦pen函數(shù)創(chuàng)建一個(gè)文件,并寫入內(nèi)容。
可以看到我們當(dāng)前的目錄當(dāng)中只有一個(gè)main.py文件,我們現(xiàn)在寫入代碼。
# 指定文件的位置,要使用字符串,可以使用絕對(duì)路徑和相對(duì)路徑 # 操作模式的選擇,我們要?jiǎng)?chuàng)建一個(gè)新的文件并寫入內(nèi)容,使用 w # 指定編碼格式為UTF-8,這是最常使用的編碼格式 # fp就是文件的IO對(duì)象,問(wèn)價(jià)句柄,用來(lái)操作文件 # i --- > input 輸入 # o --- > output 輸出 fp = open('test.txt', 'w', encoding='UTF-8') # 使用write函數(shù)寫入內(nèi)容 fp.write('Hello motherland') # 使用close函數(shù)關(guān)閉文件 fp.close()
執(zhí)行python代碼之后,我們發(fā)現(xiàn)在原來(lái)的目錄下面多出了一個(gè)名為test.txt的文件。
打開(kāi)這個(gè)文件我們就會(huì)發(fā)現(xiàn),文件中的內(nèi)容就是我們寫下的內(nèi)容。
現(xiàn)在我們重新使用 w 模式打開(kāi)這個(gè)文件,但是不操作任何東西,讓我們看看結(jié)果如何。
fp = open('test.txt', 'w', encoding='UTF-8') fp.close()
沒(méi)錯(cuò),這個(gè)文件中的內(nèi)容被清空了,這就是w模式的如果文件存在,就打開(kāi)文件并清空。
文件的讀取(讀取模式)
我們現(xiàn)在執(zhí)行下面的代碼,使用 r 模式讀取文件中的內(nèi)容。
# 使用 r 模式打開(kāi)msr.txt文件 fp = open('msr.txt', 'r', encoding='UTF-8') # 讀取文件中的內(nèi)容 res = fp.read() print(res) # 關(guān)閉文件 fp.close()
發(fā)現(xiàn)程序報(bào)錯(cuò)了,這是為什么?因?yàn)槭褂?r 模式如果指定的文件不存在就會(huì)報(bào)錯(cuò)。
那我們先創(chuàng)建一個(gè)msr.txt文件在重新讀取一下。
# 先創(chuàng)建一個(gè)msr.txt文件 fp = open('msr.txt', 'w', encoding='UTF-8') # 寫入內(nèi)容 fp.write('劉德華太帥了。') # 關(guān)閉文件 fp.close() # 然后重新讀取這個(gè)文件 fp = open('msr.txt', 'r', encoding='UTF-8') # 讀取文件中的內(nèi)容 res = fp.read() # 打印讀取的內(nèi)容 print(res) # 劉德華太帥了。 # 關(guān)閉文件 fp.close()
不再報(bào)錯(cuò)了,而且也成功的打印出來(lái)文件中的內(nèi)容。
文件內(nèi)容追加(追加模式)
追加模式如果文件不存在就創(chuàng)建文件,反之就打開(kāi)文件,但是可寫入模式的不同之處就在于,追加模式打開(kāi)文件不會(huì)清空文件中的原有的數(shù)據(jù)內(nèi)容。
打開(kāi)msr.txt文件,我們看到只有一行文字。
現(xiàn)在我們執(zhí)行下面的代碼
# 使用追加模式打開(kāi)文件 fp = open('msr.txt', 'a', encoding='UTF-8') # 在文件中寫入內(nèi)容 fp.write('但是劉德華沒(méi)有博主帥。') # 關(guān)閉文件 fp.close()
打開(kāi)文件我們看到,原有的數(shù)據(jù)并沒(méi)有被清空掉,并且寫入了新的內(nèi)容。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-monzJF4R-1647692650673)(
字節(jié)流的轉(zhuǎn)換
bytes是用來(lái)傳輸或者是存儲(chǔ)的數(shù)據(jù)格式,如果是在文件的操作過(guò)程中按照bytes的模式操作的話,就需要將數(shù)據(jù)格式轉(zhuǎn)換成為bytes流才可以。
二進(jìn)制的字節(jié)流就是底層的代碼。
使用 b 前綴
在字符串之前加上字符 b 代表是二進(jìn)制的字節(jié)流,但是范圍只是ASCII編碼,也就是說(shuō)這樣并不支持中文。
# 在字符串之前加上 b 前綴 bytechar = b'hello motherland' print(bytechar) # b'hello motherland' # 該字符串的數(shù)據(jù)類型就變成了bytes流 print(type(bytechar)) # <class 'bytes'> bytechar = b'你好祖國(guó)' # error,只能將ACSII編碼中的字符變成bytes流
使用encode函數(shù)和decode函數(shù)可以將字符串在普通字符串和字節(jié)流的形式中來(lái)回的轉(zhuǎn)換,而且可以將所有的字符變成bytes字節(jié)流,因?yàn)槟J(rèn)使用UTF-8編碼,當(dāng)然你也可以指定轉(zhuǎn)換的編碼格式。
函數(shù) | 作用 |
---|---|
encode | 將字符串轉(zhuǎn)換為二進(jìn)制的字節(jié)流 |
decode | 將二進(jìn)制的字節(jié)流轉(zhuǎn)換為字符串 |
語(yǔ)法:
- string.encode(encoding='UTF-8')
- bytes.decode(encoing='UTF-8')
var = '我的祖國(guó)' # 將字符串變成字節(jié)流,默認(rèn)使用UTF-8編碼 res = var.encode() print(res) # b'\xe6\x88\x91\xe7\x9a\x84\xe7\xa5\x96\xe5\x9b\xbd' print(type(res)) # <class 'bytes'> # 指定編碼格式UTF-8 res = var.encode(encoding='UTF-8') print(res) # b'\xe6\x88\x91\xe7\x9a\x84\xe7\xa5\x96\xe5\x9b\xbd' # 可以看到指定為UTF-8編碼的和默認(rèn)的結(jié)果是一樣的 # 指定編碼格式為ASCII res = var.encode('ASCII') # error # 因?yàn)樵址侵形?,所以不能使用ASCII編碼 # 指定為GBK編碼 res = var.encode('GBK') print(res) # b'\xce\xd2\xb5\xc4\xd7\xe6\xb9\xfa' # 可以看到GBK的編碼中文可以節(jié)省更多的空間 # 可以使用 len 函數(shù)檢測(cè)字節(jié)流的長(zhǎng)度 print(len(res)) # 8 # 解碼 var = res.decode() # error # 應(yīng)為默認(rèn)使用的UTF-8解碼,但是res的編碼格式是GBK,所以失敗 # 只能使用對(duì)應(yīng)的編碼格式解碼 var = res.decode('GBK') print(var) # 我的祖國(guó)
存儲(chǔ)二進(jìn)制的字節(jié)流
如果在操作文件的時(shí)候要使用字節(jié)流的方式,在使用open函數(shù)選擇模式的時(shí)候要加上 b ,表示進(jìn)行字節(jié)流的操作,然后open函數(shù)就不能在指定編碼格式了,因?yàn)楝F(xiàn)在的操作的都是字節(jié)流,然而字節(jié)流本身就已經(jīng)是指定的編碼格式編碼過(guò)了。
# 如果指定了字節(jié)流模式還要指定encoding參數(shù)就會(huì)報(bào)錯(cuò) fp = open('test.txt', 'wb', encoding='UTF-8') # error # 使用字節(jié)流模式只能進(jìn)行字節(jié)流的寫入 fp = open('test.txt', 'wb') # fp.write('hello motherland') # error,不能直接使用字符串 fp.write(b'hello motherland') fp.write('我的祖國(guó)'.encode()) fp.close()
寫在文件中的內(nèi)容還是原來(lái)的樣子,不是字節(jié)流的形式
注意事項(xiàng):
- 使用字節(jié)流模式編輯過(guò)的文件只能使用字節(jié)流模式去操作
- 使用什么格式的字節(jié)流寫入文件的內(nèi)容,讀取的時(shí)候只能使用對(duì)應(yīng)的編碼格式去解碼
- 任何文件都可以使用字節(jié)流模式去讀取內(nèi)容,讀取的內(nèi)容是字節(jié)流,如果這個(gè)文件是按照某個(gè)編碼格式寫入的,解碼需要使用對(duì)應(yīng)的編碼格式;如果這個(gè)文件的內(nèi)容不是使用字節(jié)流模式寫入,讀取的字節(jié)流默認(rèn)是UTF-8格式的。
上下文管理器
在python中,有一些任務(wù)是當(dāng)你開(kāi)啟之后,結(jié)束的時(shí)候需要專門的關(guān)閉任務(wù),比如文件操作,在結(jié)束操作的使用需要使用close()函數(shù)專門的關(guān)閉文件、結(jié)束任務(wù),這樣就很繁瑣,所以python中推出了 with …… as ……
的語(yǔ)法,在 with 代碼塊中如果結(jié)束操作,不需要在專門的結(jié)束任務(wù)。
上下文管理器,任何需要進(jìn)行上下文操作的對(duì)象,都可以使用此語(yǔ)法。
語(yǔ)法:with 任務(wù) as 操作句柄:
# 不需要在使用close()函數(shù)專門的關(guān)閉文件,結(jié)束任務(wù)了 with open('test.txt', 'wb') as fp: fp.write(b'hello motherland')
刷新緩沖區(qū)
我們學(xué)習(xí)了這么久,每次都一定要關(guān)閉文件、結(jié)束任務(wù),這樣做的意義是什么?
比較直觀的目的就是為了保存文件,但是好奇的我們?cè)缇蜏y(cè)試了不使用close()關(guān)閉文件,寫入的內(nèi)容一樣是保存了文件中的,這是怎么回事?
看下面的代碼,發(fā)現(xiàn)我們文件中依然是保存了我們寫入的內(nèi)容。
fp = open('test.txt', 'w', encoding='UTF-8') fp.write('我和我的祖國(guó),就像是海和浪花一朵。')
這是因?yàn)?,關(guān)閉文件的根本目的是為了刷新緩沖區(qū),然而刷新緩沖區(qū)的方法不止一種。
- 當(dāng)文件關(guān)閉的時(shí)候自動(dòng)刷新緩沖區(qū)
- 當(dāng)整個(gè)程序運(yùn)行結(jié)束的時(shí)候自動(dòng)刷新緩沖區(qū)
- 當(dāng)緩沖區(qū)寫滿還自動(dòng)刷新緩沖區(qū)
- 手動(dòng)刷新緩沖區(qū)
刷新緩沖區(qū)的意義在于最后的保存文件,就好像在使用文檔編輯器的時(shí)候,雖然寫滿內(nèi)容,但最后不點(diǎn)擊保存按鈕內(nèi)容也不會(huì)保存下來(lái)。
而我們上面的例子就是因?yàn)槌绦蜻\(yùn)行結(jié)束的時(shí)候自動(dòng)刷新了緩沖區(qū),所以才保存了寫入文件的內(nèi)容,而close的作用就是關(guān)閉文件,關(guān)閉文件也可以刷新緩沖區(qū),所以這就是每次要關(guān)閉文件的原因所在,為了防止自動(dòng)刷新的失敗。
那么什么情況之下程序就沒(méi)有辦法執(zhí)行完呢?
比如說(shuō)程序的意外中斷、或者是死循環(huán),下面的代碼中就是因?yàn)樗姥h(huán)的原因?qū)е鲁绦驔](méi)有辦法執(zhí)行完成,而沒(méi)有保存新寫入的內(nèi)容。
下面的代碼,先是寫入了內(nèi)容,然后就是一個(gè)死循環(huán),這樣程序永遠(yuǎn)都不會(huì)執(zhí)行完成,就不能自動(dòng)的刷新緩沖區(qū),如果程序意外中斷,內(nèi)容也不會(huì)寫入文件當(dāng)中,你可以將程序運(yùn)行起來(lái)之后,強(qiáng)制中斷測(cè)試一下,會(huì)發(fā)現(xiàn)是一個(gè)空文件。
with open('test.txt', 'w', encoding='UTF-8') as fp: fp.write('我和我的祖國(guó),一刻也不能分割。') while True: pass
手動(dòng)刷新
上面的例子中,文件沒(méi)有辦法關(guān)閉,程序沒(méi)有辦法執(zhí)行完成,貌似緩沖區(qū)也很難寫滿,難道我們的內(nèi)容就沒(méi)有辦法保存了嗎?
你機(jī)智的寫上了一行代碼,是close()函數(shù),這樣就關(guān)閉了文件,就可以將死循環(huán)之前的內(nèi)容保存了嘛。
with open('test.txt', 'w', encoding='UTF-8') as fp: fp.write('我和我的祖國(guó),一刻也不能分割。') fp.close() # 關(guān)閉文件 while True: pass
你經(jīng)過(guò)測(cè)試,上面的代碼的確的保存了寫入的內(nèi)容,但是我們關(guān)閉了文件,再次操作文件的時(shí)候就必須重新開(kāi)啟文件,不然沒(méi)有辦法繼續(xù)操作。
with open('test.txt', 'w', encoding='UTF-8') as fp: fp.write('我和我的祖國(guó),一刻也不能分割。') fp.close() fp.write('我和我的祖國(guó),就像是海和浪花一朵。') # error,文件已經(jīng)關(guān)閉 while True: pass
發(fā)現(xiàn)寫入的第二條內(nèi)容根本就沒(méi)法執(zhí)行了,怎么辦?使用fiush()函數(shù)手動(dòng)刷新緩沖區(qū)。
with open('test.txt', 'w', encoding='UTF-8') as fp: fp.write('我和我的祖國(guó),一刻也不能分割。') fp.flush() fp.write('我和我的祖國(guó),就像是海和浪花一朵。') fp.flush() while True: pass
發(fā)現(xiàn)手動(dòng)刷新將內(nèi)容保存了下來(lái),而且沒(méi)有影響程序的執(zhí)行。以后如果程序任務(wù)過(guò)大,沒(méi)有執(zhí)行完成就意外中斷,這樣就有一點(diǎn)數(shù)據(jù)保存不下來(lái)的風(fēng)險(xiǎn),我們就可以隔著一段任務(wù)手動(dòng)刷新一下,就不至于將所有的數(shù)據(jù)全部丟失。
文件的擴(kuò)展模式
我們經(jīng)過(guò)上面的學(xué)習(xí),用到了寫、讀、手動(dòng)刷新、關(guān)閉文件等幾種操作文件的函數(shù),但是除此之外,還有一些常用的相關(guān)函數(shù)。
函數(shù) | 作用 |
---|---|
write | 寫入數(shù)據(jù) |
read | 讀取數(shù)據(jù) |
fiush | 手動(dòng)刷新緩沖區(qū) |
close | 關(guān)閉文件 |
seek | 調(diào)整指針(光標(biāo))的位置 |
tell | 返回當(dāng)前指針左側(cè)所有的字節(jié)數(shù) |
readable | 判斷文件對(duì)象是否可讀 |
writeable | 判斷文件對(duì)象是否可寫 |
readline | 讀取文件的一行內(nèi)容 |
readlines | 將文件中的內(nèi)容按照換行讀取到列表當(dāng)中 |
writelines | 將內(nèi)容是字符串的可迭代數(shù)據(jù)寫入文件當(dāng)中 |
truncate | 把要截取的字符串提取出來(lái),然后清空內(nèi)容并將截取的內(nèi)容重新寫入 |
read的使用
plus增強(qiáng)模式的使用
在open函數(shù)中,使用 + 號(hào),進(jìn)入增強(qiáng)模式,可讀可寫。
我們現(xiàn)在使用 r+ 模式打開(kāi)之前的文件,讀取其中的內(nèi)容。
with open('test.txt', 'r+', encoding='UTF_8') as fp: # 讀取內(nèi)容 res = fp.read() print(res) # 我和我的祖國(guó),一刻也不能分割。我和我的祖國(guó),就像是海和浪花一朵。 # 可以指定字符的個(gè)數(shù),讀取指定個(gè)數(shù)的字符 res = fp.read(5) print(res) #
發(fā)現(xiàn)什么第二遍沒(méi)有讀取出任何的內(nèi)容,我們重新打開(kāi)一遍文件,重新讀取。
with open('test.txt', 'r+', encoding='UTF_8') as fp: # 讀取五個(gè)字符 res = fp.read(5) print(res) # 我和我的祖 # 再讀取五個(gè)字符 res = fp.read(5) print(res) # 國(guó),一刻也
發(fā)現(xiàn)第二遍讀取的內(nèi)容是接著第一遍讀取的內(nèi)容之后的,我們重新打開(kāi)一遍文件,寫一些內(nèi)容。
with open('test.txt', 'r+', encoding='UTF_8') as fp: # 寫入內(nèi)容 fp.write('我永遠(yuǎn)和我的祖國(guó)在一起') # 讀取其中的內(nèi)容 res = fp.read() print(res) # 能分割。我和我的祖國(guó),就像是海和浪花一朵。
讀取內(nèi)容的時(shí)候,發(fā)現(xiàn)沒(méi)有我們寫入的內(nèi)容,而且讀取的文件內(nèi)容怎么看起來(lái)好怪異的感覺(jué)啊,怎么少了些內(nèi)容?
我們重新打開(kāi)文件讀取一遍
with open('test.txt', 'r+', encoding='UTF_8') as fp: res = fp.read() print(res) # 我永遠(yuǎn)和我的祖國(guó)在一起能分割。我和我的祖國(guó),就像是海和浪花一朵。
為什么我們的寫入的內(nèi)容在文件的開(kāi)頭,而且還替換掉了原有的一部分?jǐn)?shù)據(jù)?我們上面的一系列操作為什么那么的奇怪?
這都是因?yàn)楣鈽?biāo)的作用在做怪。
光標(biāo)的作用
還記得我們之前介紹四種打開(kāi)模式的時(shí)候嗎?寫入模式光標(biāo)在文檔最后,讀取模式光標(biāo)在文檔最前,追加模式光標(biāo)在文檔最前,異或模式光標(biāo)在文檔最前。
寫入的內(nèi)容和讀取的內(nèi)容都是從光標(biāo)的位置開(kāi)始的。
read()函數(shù)默認(rèn)讀取光標(biāo)一右側(cè)所有的內(nèi)容。而不是文檔中的所有內(nèi)容,之前的測(cè)試之所以可以一次性的讀取出所有的內(nèi)容是因?yàn)槲覀兇蜷_(kāi)文檔使用的是讀取模式,光標(biāo)的位置在文檔的開(kāi)頭。光標(biāo)會(huì)隨著讀取的內(nèi)容而移動(dòng),讀取到哪個(gè)字符光標(biāo)就移動(dòng)到哪個(gè)字符的后面。
write()寫入內(nèi)容的時(shí)候是覆蓋模式。我們都知道我們的計(jì)算機(jī)系統(tǒng)中的文本輸入方式是有兩種的,使用insert鍵就可以切換著兩種模式。一種是插入模式,一種是覆蓋模式。
插入模式是我們平常最經(jīng)常使用的,比如說(shuō)我們打開(kāi)一個(gè)文本編輯軟件,隨便的寫入一段內(nèi)容,然后把光標(biāo)移動(dòng)到文檔的開(kāi)頭,寫入內(nèi)容,發(fā)現(xiàn)新的內(nèi)容是插入到了舊的內(nèi)容之前的,舊的內(nèi)容不會(huì)消失,而是后移,這就是插入模式;然后重新將光標(biāo)移動(dòng)到文檔的開(kāi)頭,然后按下insert鍵,這個(gè)時(shí)候你的輸入方式就變成了覆蓋模式,現(xiàn)在的你每當(dāng)輸入一個(gè)新的字符就會(huì)覆蓋掉后面的一個(gè)舊字符,這就是覆蓋模式,python的文本編輯就是這種覆蓋模式。光標(biāo)隨著寫入的內(nèi)容向后移動(dòng)。
光標(biāo)位置的移動(dòng)
我們剛才的時(shí)候就了解到了有一個(gè)可以調(diào)整光標(biāo)位置的函數(shù),叫做seek,使用這個(gè)函數(shù)我們可以隨意的調(diào)節(jié)光標(biāo)的位置,從而編輯文件的時(shí)候可以更加的隨心所欲。
seek(offset: int, [whence: int = 0])
seek(偏移量, [基準(zhǔn)位置])
seek函數(shù)的兩個(gè)參數(shù)都是整型。
第一個(gè)參數(shù)表示的是偏移量,單獨(dú)使用時(shí)表示將光標(biāo)移動(dòng)到從文檔的開(kāi)頭算起的第N個(gè)字節(jié)的位置后;
第二個(gè)參數(shù)表示的光標(biāo)的位置,使用的時(shí)候只有0、1、2三個(gè)選型,且偏移量必須為0;
0代表的是文檔的最開(kāi)端
1代表的是光標(biāo)的當(dāng)前位置
2代表的是文檔的最后端
# 先使用 w+ 模式打開(kāi)一個(gè)文件,這個(gè)時(shí)候的文件為空,光標(biāo)在文檔的開(kāi)頭位置 with open('test.txt', 'w+', encoding='UTF-8') as fp: # 我們寫入內(nèi)容,這個(gè)時(shí)候光標(biāo)的位置隨著寫入的內(nèi)容到了文檔的最后 fp.write('hello motherland.') # 所以現(xiàn)在的光標(biāo)的右側(cè)沒(méi)有任何一個(gè)字節(jié)符,所以讀不出任何的內(nèi)容 res = fp.read() print(repr(res)) # '' # 所以光標(biāo)還是在最后的位置,使用seek切換光標(biāo)的位置為開(kāi)頭,讀取剛才寫入的內(nèi)容 fp.seek(0) res = fp.read() print(repr(res)) # 'hello motherland.' # 讀取完內(nèi)容之后,光標(biāo)又到了文檔的最后的位置,調(diào)整到開(kāi)頭的第五個(gè)字節(jié)符的位置 fp.seek(5) # 再次讀取文件的內(nèi)容,這一次只讀取5個(gè)字符,發(fā)現(xiàn)前五個(gè)字符沒(méi)有了 res = fp.read(5) print(repr(res)) # ' moth' # 現(xiàn)在光標(biāo)在文檔的第十個(gè)字符位置,我們將光標(biāo)切換到文檔的最后,然后讀取文檔發(fā)現(xiàn)什么內(nèi)容也沒(méi)有 fp.seek(0, 2) res = fp.read() print(repr(res)) # ''
注意到了嗎?我說(shuō)的seek移動(dòng)的是字節(jié)的數(shù)量,什么是字節(jié)的數(shù)量?
我們之前說(shuō)的不同的編碼格式對(duì)于不同的字符都是不一樣的,但是所有的編碼格式對(duì)于英文字母為主的一些的字符都是一個(gè)字節(jié)的大小,但是漢字不一樣,漢字在GB中是兩個(gè)字節(jié)、UTF中是三個(gè)字節(jié)、Unicode中是四個(gè)字節(jié)。
seek的偏移單位是字節(jié),不是字符,所以在使用seek在操作bytes字節(jié)流時(shí),要注意移動(dòng)的間隔,因?yàn)橐苿?dòng)的是字節(jié)位數(shù),而在GB編碼中一個(gè)漢字兩個(gè)字節(jié),在Unicode(UTF-8)中,一個(gè)漢字三個(gè)字節(jié),如果seek將指針移動(dòng)至漢字之間,就會(huì)導(dǎo)致讀取時(shí)漢字的編碼不完整而導(dǎo)致錯(cuò)誤。
# 重新寫入一個(gè)文件,注意我們的編碼格式 with open('test.txt', 'w+', encoding='UTF-8') as fp: fp.write('我和我的祖國(guó),一刻也不能分割。我和我的祖國(guó),就像是海和浪花一朵。') # 我們現(xiàn)在讀取除了第一句話之后的內(nèi)容,前面的內(nèi)容一共是15個(gè)字符,我們使用seek跳過(guò)去 fp.seek(15) res = fp.read() print(repr(res)) # '國(guó),一刻也不能分割。我和我的祖國(guó),就像是海和浪花一朵。' # 咦?怎么只跳過(guò)了五個(gè)漢字?因?yàn)槲覀兪褂玫腢TF-8的編碼,一個(gè)漢字由3個(gè)字節(jié),真好是15個(gè)單位 # 你是幸運(yùn)的,如果我們?cè)谟乙埔粋€(gè)字節(jié)的單位,就是一個(gè)漢字都沒(méi)有完全遷移完會(huì)怎么樣? fp.seek(1) # res = fp.read() # print(res) # error, 報(bào)錯(cuò)了,因?yàn)槭O碌淖址皇峭暾?,所以沒(méi)有辦法讀出,就報(bào)錯(cuò)了
# 就像是你好的UTF-8編碼是六個(gè)字節(jié)組成的, print('你好'.encode()) # b'\xe4\xbd\xa0\xe5\xa5\xbd' # 如果去掉了一個(gè)字節(jié),就是不完整的了,還能解碼出來(lái)嗎? print(b'\xbd\xa0\xe5\xa5\xbd'.decode()) # error,解碼失敗
所以在使用seek函數(shù)的時(shí)候一定要慎用。
tell的使用
# tell 當(dāng)前光標(biāo)左側(cè)所有的字節(jié)數(shù)(返回字節(jié)數(shù)) # 使用閱讀模式打開(kāi)文件 with open('test.txt', 'r+', encoding='UTF-8') as fp: # 使用tell函數(shù)查看貫標(biāo)左側(cè)的字節(jié)數(shù) res = fp.tell() print(res) # 0 # 因?yàn)殚喿x模式的光標(biāo)在文件的開(kāi)頭,所以返回0個(gè)字節(jié)數(shù) # 使用seek將光標(biāo)移動(dòng)到文檔的末尾 fp.seek(0, 2) # 使用tell查看整個(gè)文檔的字節(jié)數(shù),這就是文檔的大小 res = fp.tell() print(res) # 96 # 快去看看你的文件信息中的文件大小是不是96字節(jié)的?
其它的相關(guān)函數(shù)
判斷文件對(duì)象可讀可寫
# 使用 r+ 模式打開(kāi)文件 with open('test.txt', 'r+', encoding='UTF-8') as fp: # 使用readable 和 readable 查看這個(gè)文檔是否可讀可寫 if fp.readable(): print('本文檔可讀') if fp.writable(): print('本文檔可寫') ''' 結(jié)果: 本文檔可讀 本文檔可寫 '''
readline
讀取一行內(nèi)容
# 打開(kāi)文件,重新寫入多行內(nèi)容 with open('test.txt', 'w+', encoding='UTF-8') as fp: # 可以使用多行字符串 fp.write('''11111 22222 33333 ''') # 也可以使用轉(zhuǎn)義字符進(jìn)行換行 fp.write('44444\n55555\n66666')
with open('test.txt', 'r+', encoding='UTF-8') as fp: # 使用read讀取的整個(gè)文檔的內(nèi)容 res = fp.read() print(res) ''' 結(jié)果: 11111 22222 33333 44444 55555 66666 ''' # 使用readline讀取一一行的內(nèi)容 fp.seek(0) res = fp.readline() print(res) # 11111 # 再讀取一行 res = fp.readline() print(res) # 22222 # 可以指定讀取的字符個(gè)數(shù) res = fp.readline(3) print(res) # 333 # 如果指定的個(gè)數(shù)大于本行的字符個(gè)數(shù),就讀取本行所有的內(nèi)容 res = fp.readline(1000) print(res) # 33 # 為什么是33不是44444?因?yàn)閞eadline也要受到光標(biāo)的影響
readlines
將文件中的內(nèi)容按照換行讀取到列表中
with open('test.txt', 'r+', encoding='UTF-8') as fp: res = fp.readlines() print(res) # ['11111\n', '22222\n', '33333\n', '44444\n', '55555\n', '66666'
注意:readlines不會(huì)影響光標(biāo)的移動(dòng),但是讀取的是光標(biāo)的右側(cè)數(shù)據(jù);而且readlines的讀取將換行符也讀取上了,因?yàn)閾Q行符本身也是一行的內(nèi)容。
按行讀取內(nèi)容我一般使用到readlines
函數(shù),但是也可以使用其它的方法,比如直接遍歷open
實(shí)例化對(duì)象,open
實(shí)例化對(duì)象本身就是一個(gè)可迭代對(duì)象,它將文件中的內(nèi)容按照換行符分開(kāi)。
with open('text.txt', 'r', encoding='UTF-8') as fp: for line in fp: print(line)
writelines
將內(nèi)容是字符串的可迭代性數(shù)據(jù)寫入文件中,writelines不會(huì)根據(jù)元素?fù)Q行。
lst = ['china', 'america', 'russia'] with open('test.txt', 'w+', encoding='UTF-8') as fp: # 使用writelines寫入內(nèi)容 fp.writelines(lst) fp.seek(0) # 讀取數(shù)據(jù) res = fp.readlines() print(res) # ['chinaamericarussia']
truncate
文件中的內(nèi)容只保留截取的內(nèi)容。
從文件開(kāi)頭開(kāi)始,截取指定字節(jié)長(zhǎng)度的內(nèi)容,然后將文件清空,然后將截取的內(nèi)容重新填入文件中。
# 打開(kāi)一個(gè)文件 with open('test.txt', 'w+', encoding='UTF-8') as fp: # 寫入一段內(nèi)容 fp.write('1234567890') # 保留截取的內(nèi)容,只保留前5個(gè)字節(jié)的內(nèi)容 fp.truncate(5) # 查看文件的內(nèi)容 fp.seek(0) res = fp.read() print(res) # 12345
關(guān)于生成文件MD5心得
我在工作時(shí)需要給調(diào)用翻譯狗的一個(gè)API,用于上傳文獻(xiàn)并翻譯返回,但是對(duì)方需要文件MD5進(jìn)行驗(yàn)證,我們需要在接入接口的時(shí)候,需要將文件md5傳入,這個(gè)時(shí)候就出現(xiàn)了一些問(wèn)題,我在傳入文件和文件MD5的時(shí)候,被對(duì)方回應(yīng)文件MD5
不匹配,我很好奇,為什么會(huì)出現(xiàn)這樣的情況?
我在使用這個(gè)接口當(dāng)中,有好幾處地方比如token的生成和文件md5的地方都會(huì)需要md5加密,所以為此我們專門將生成md5的代碼封裝成為一個(gè)函數(shù)(將字符串輸入,返回md5,代碼如下:
import hashlib def enMD5(target): """ MD5加密 """ res = hashlib.md5(target.encode()).hexdigest() return res
python中生成md5需要輸入字節(jié)流格式的數(shù)據(jù),而我一開(kāi)始只有字符串的數(shù)據(jù)需要使用md5加密,所以我在函數(shù)中將字符串變成字節(jié)流。token就是傳入字符串得到的。
但是文件md5的話可以直接讀出字節(jié)流的格式,但是因?yàn)樵偈褂眠@個(gè)函數(shù)不方便,所以我使用正常讀取文檔的方式讀取文件中的內(nèi)容,然后放入函數(shù)中,結(jié)果就是上面說(shuō)的,和對(duì)方得出的文件md5并不匹配。我自認(rèn)為我的代碼是沒(méi)有問(wèn)題的,于是我們依次查找問(wèn)題的所在,后來(lái)我發(fā)現(xiàn)網(wǎng)上很多博主的方法都是直接從文件讀取二進(jìn)制字節(jié)流的方式獲取的,我實(shí)在是沒(méi)有辦法了,就想會(huì)不會(huì)就是讀取方式的問(wèn)題呢?果然,我就發(fā)現(xiàn)不同格式讀取的出來(lái)的結(jié)果是不同的,測(cè)試的案例如下:
with open(file_path, 'w', encoding='UTF-8') as fp: fp.write('msr\nhello\r\nmotherland.') with open(file_path, 'rb') as fp: print(fp.read()) with open(file_path, 'r', encoding='UTF-8') as fp: print(r'f', repr(fp.read()), sep='')
上述的結(jié)果為:
b'msr\r\nhello\r\r\nmotherland.'
f'msr\nhello\n\nmotherland.'
沒(méi)錯(cuò),我發(fā)現(xiàn)直接使用b
模式和普通模式讀取內(nèi)容然后轉(zhuǎn)化成為bytes
的結(jié)果是不同的,那么也必將導(dǎo)致最后文件md5是不正確的。大家也看到了,不管是哪一種讀取的方法其實(shí)和我寫入的內(nèi)容都是不同的,在本次的測(cè)試案例當(dāng)中對(duì)于換行有著不同的認(rèn)知,讀取的原因我沒(méi)有深入了解,但是我注意到了官方文檔中說(shuō)b
模式就是專門讀取文件字節(jié)流格式的,所以以后大家生成文件md5的時(shí)候,一定要直接使用b
模式讀取文件內(nèi)容。
上述的測(cè)試環(huán)境是:
python: python3.6.8_win_x64(Cpython)
system: windows_10_x64
到此這篇關(guān)于Python IO文件管理的具體使用的文章就介紹到這了,更多相關(guān)Python IO文件管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python3+PyQt5 實(shí)現(xiàn)Rich文本的行編輯方法
今天小編就為大家分享一篇python3+PyQt5 實(shí)現(xiàn)Rich文本的行編輯方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06python 利用pyttsx3文字轉(zhuǎn)語(yǔ)音過(guò)程詳解
這篇文章主要介紹了python 利用pyttsx3文字轉(zhuǎn)語(yǔ)音過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-094種方法教你利用Python發(fā)現(xiàn)數(shù)據(jù)的規(guī)律
發(fā)現(xiàn)數(shù)據(jù)的規(guī)律是數(shù)據(jù)分析和數(shù)據(jù)科學(xué)中非常重要的一個(gè)步驟。這篇文章主要給大家整理了4個(gè)可以發(fā)現(xiàn)數(shù)據(jù)規(guī)律的方法,希望對(duì)大家有所幫助2023-03-03Python讀取本地文件并解析網(wǎng)頁(yè)元素的方法
今天小編就為大家分享一篇Python讀取本地文件并解析網(wǎng)頁(yè)元素的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05python編程的核心知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于python編程的核心知識(shí)點(diǎn)總結(jié)內(nèi)容,對(duì)此有興趣的朋友們可以學(xué)習(xí)參考下。2021-02-02Python使用Flask框架實(shí)現(xiàn)文件上傳實(shí)例
這篇文章主要介紹了Python使用Flask庫(kù)文件上傳實(shí)例,用?Flask?處理文件上傳很容易,只要確保HTML表單中設(shè)置enctype="multipart/form-data"屬性就可以了,需要的朋友可以參考下2023-08-08