IO操作與讀寫文件
讀寫文件是最常見的IO操作描验。Python內(nèi)置了讀寫文件的函數(shù)占哟,用法和C是兼容的。不論哪種翩迈,一定要注意編碼和解碼的一致性持灰,否則會(huì)出現(xiàn)亂碼或報(bào)錯(cuò)。
打開文件
python中文件的讀取封裝后负饲,使用時(shí)非常簡(jiǎn)單搅方,讀取文件調(diào)用open()
函數(shù)绽族,返回一個(gè)文件對(duì)象吧慢。例如:
my_test_file = open("io_test.txt", 'r')
-
打開文件:open()函數(shù)
open()函數(shù)詳細(xì)解釋:
open()函數(shù)返回一個(gè)文件對(duì)象涛漂,最常用的有兩個(gè)參數(shù):open(filename, mode)
第一個(gè)參數(shù)是包含文件名的字符串。
第二個(gè)參數(shù)是另一個(gè)字符串检诗,其中包含一些描述文件使用方式的字符匈仗。 模式可以是'r'
僅讀取文件時(shí),'w'
僅寫入(將刪除同名的現(xiàn)有文件)逢慌,并 'a'
打開文件進(jìn)行追加; 寫入文件的任何數(shù)據(jù)都會(huì)自動(dòng)添加到最后悠轩。 'r+'
打開文件進(jìn)行讀寫火架。所述模式參數(shù)是可選的; 'r'
將被假設(shè)鉴象,如果它被省略。
通常何鸡,文件以文本模式打開淆游,這意味著從文件讀取和寫入文件,這些文件以特定編碼進(jìn)行編碼隔盛。如果未指定編碼犹菱,則默認(rèn)值取決于平臺(tái)。'b'
附加到模式后以二進(jìn)制模式打開文件 :現(xiàn)在以bytes對(duì)象的形式讀取和寫入數(shù)據(jù)吮炕。此模式應(yīng)用于所有不包含文本的文件已亥。如下表所示:
在文本模式下,讀取時(shí)的默認(rèn)設(shè)置是將平臺(tái)特定的行結(jié)尾(\n
在Unix上来屠,\r\n
在Windows上)轉(zhuǎn)換為just \n
虑椎。在文本模式下寫入時(shí),默認(rèn)設(shè)置是將事件的發(fā)生轉(zhuǎn)換\n
為特定于平臺(tái)的行結(jié)尾俱笛。這幕后的修改文件數(shù)據(jù)精細(xì)的文本文件捆姜,但會(huì)喜歡,在破壞了二進(jìn)制數(shù)據(jù) JPEG
或EXE
文件迎膜。在讀取和寫入此類文件時(shí)要非常小心地使用二進(jìn)制模式泥技。
# 打開文件:第一種寫法
try:
my_test_file = open("io_test.txt", 'r')
# content = my_test_file.read()
# print(content)
finally:
if my_test_file:
my_test_file.close()
# 打開文件:第二種寫法
with open('io_test.txt', 'r') as f:
# print('f:', f.read() + '\t \t')
lines = f.readlines()
for index, line in enumerate(lines):
print('第{0}行:{1}'.format(index, line))
# 以二進(jìn)制方式打開圖片
with open('2.jpg', 'r') as pic:
print( pic.read())
with
在處理文件對(duì)象時(shí),最好使用關(guān)鍵字磕仅。優(yōu)點(diǎn)是文件在套件完成后正確關(guān)閉珊豹,即使在某個(gè)時(shí)刻引發(fā)了異常。使用with
也比寫相當(dāng)于短得多try...finally
塊:
如果您沒有使用該with
關(guān)鍵字榕订,那么您應(yīng)該調(diào)用 f.close()
以關(guān)閉該文件并立即釋放它使用的任何系統(tǒng)資源店茶。如果您沒有顯式關(guān)閉文件,Python的垃圾收集器最終將銷毀該對(duì)象并為您關(guān)閉打開的文件劫恒,但該文件可能會(huì)保持打開狀態(tài)一段時(shí)間贩幻。另一個(gè)風(fēng)險(xiǎn)是不同的Python實(shí)現(xiàn)將在不同的時(shí)間進(jìn)行清理。
通過with
語(yǔ)句或通過調(diào)用關(guān)閉文件對(duì)象后f.close()
两嘴,再嘗試使用該文件對(duì)象將自動(dòng)失敗丛楚。會(huì)報(bào)錯(cuò):
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
文件對(duì)象的方法
打開文件后,就要進(jìn)行讀寫操作了憔辫。本小節(jié)中的下面的示例將假定f
是已創(chuàng)建一個(gè)名為f的文件對(duì)象 趣些,即有:
with open('io_test.txt', 'r') as f:
-
f.read(size)讀取文件的內(nèi)容
要讀取文件的內(nèi)容,請(qǐng)調(diào)用f.read(size)
贰您,讀取一些數(shù)據(jù)并將其作為字符串(在文本模式下)或字節(jié)對(duì)象(在二進(jìn)制模式下)返回坏平。 size
是可選的數(shù)字參數(shù)拢操。當(dāng)省略不傳size
或?yàn)樨?fù)數(shù)時(shí),將讀取并返回文件的全部?jī)?nèi)容; 如果文件的大小是機(jī)器內(nèi)存的兩倍功茴,那么這就是你的問題庐冯。否則孽亲,最多讀取并返回大小字節(jié)坎穿。如果已到達(dá)文件末尾,f.read()則返回空字符串('')返劲。
io_test.txt
文件中的內(nèi)容是:
2018年世界杯半決賽全部結(jié)束玲昧,7月15日23:00法國(guó)和克羅地亞將會(huì)師決賽競(jìng)逐大力神杯,7月14日22:00比利時(shí)與英格蘭隊(duì)爭(zhēng)奪第三名篮绿。
例1:當(dāng)省略不傳size
或?yàn)樨?fù)數(shù)時(shí)孵延,將讀取并返回文件的全部?jī)?nèi)容:
with open("io_test.txt", encoding='utf-8') as f:
content_str = f.read()
print(content_str)
或者:
with open("io_test.txt", encoding='utf-8') as f:
content_str = f.read(-1)
print(content_str)
運(yùn)行結(jié)果都是:
2018年世界杯半決賽全部結(jié)束,7月15日23:00法國(guó)和克羅地亞將會(huì)師決賽競(jìng)逐大力神杯亲配,7月14日22:00比利時(shí)與英格蘭隊(duì)爭(zhēng)奪第三名尘应。
-
f.readline()讀取一行內(nèi)容
readline()函數(shù)詳細(xì)解釋:
readline()函數(shù)返回一個(gè)從文件中讀取一行內(nèi)容,可以省略參數(shù)或者傳入一個(gè)參數(shù):readline(limit)
limit
參數(shù)表示:最大限制可讀取limit個(gè)字符吼虎。如果實(shí)際讀取到的內(nèi)容字符數(shù)小于等于傳入的limit的值犬钢,則按照實(shí)際讀入的返回,否則就只返回limit個(gè)字符(且此時(shí)下一行內(nèi)容按照后面未讀取到的內(nèi)容開始算起)思灰。
f.readline()
從文件中讀取一行; 換行符\n
留在字符串的末尾玷犹,如果文件沒有以換行符結(jié)尾,則只在文件的最后一行省略洒疚。這使得返回值明確無(wú)誤歹颓。
如果f.readline()
返回一個(gè)空字符串,則表示已到達(dá)文件末尾油湖;如果f.readline()
返回一個(gè)\n
只包含一個(gè)換行符的字符串巍扛,則表示當(dāng)前行是空行。
再次提示:
當(dāng)f.readline()
讀到空行時(shí)乏德,返回結(jié)果是:\n
电湘;當(dāng)f.readline()
讀到文件末尾時(shí),返回結(jié)果是:空字符串''
鹅经。
項(xiàng)目中有文件io_test.txt
內(nèi)容如下:
2018年俄羅斯世界杯是第21屆世界杯足球賽寂呛。
2022年卡塔爾世界杯是第22屆世界杯足球賽。
readline()
函數(shù)不傳入?yún)?shù)時(shí)瘾晃,編寫測(cè)試代碼:
with open('io_test.txt', encoding='utf-8') as f:
s1 = f.readline()
print(s1)
s2 = f.readline()
print(s2)
運(yùn)行結(jié)果:
2018年俄羅斯世界杯是第21屆世界杯足球賽贷痪。
2022年卡塔爾世界杯是第22屆世界杯足球賽。
readline()
函數(shù)傳入?yún)?shù)limit時(shí)蹦误,編寫測(cè)試代碼:
with open('io_test.txt', encoding='utf-8') as f:
s1 = f.readline(4)
print(s1)
s2 = f.readline()
print(s2)
s3 = f.readline()
print(s3)
運(yùn)行結(jié)果如下:
2018
年俄羅斯世界杯是第21屆世界杯足球賽劫拢。
2022年卡塔爾世界杯是第22屆世界杯足球賽肉津。
即:如果實(shí)際讀取到的內(nèi)容字符數(shù)小于等于傳入的limit的值,則按照實(shí)際讀入的返回舱沧,否則就只返回limit個(gè)字符(且此時(shí)下一行內(nèi)容按照后面未讀取到的內(nèi)容開始算起)妹沙。
-
f.readlines()函數(shù):讀取文件中的所有行
如果要讀取文件的所有行,使用 f.readlines()熟吏,返回該文件中包含的所有行距糖。
如果設(shè)置可選參數(shù) sizehint, 則讀取指定長(zhǎng)度的字節(jié), 并且將這些字節(jié)按行分割。牵寺。
# readlines
with open("io_test.txt", encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
print(line)
運(yùn)行結(jié)果:
2018年俄羅斯世界杯是第21屆世界杯足球賽悍引。
2022年卡塔爾世界杯是第22屆世界杯足球賽。
另一種方式是迭代一個(gè)文件對(duì)象然后讀取每行:
with open("io_test.txt", encoding="utf-8") as f:
for line in f:
print(line,end='')
運(yùn)行結(jié)果:
2018年俄羅斯世界杯是第21屆世界杯足球賽帽氓。
2022年卡塔爾世界杯是第22屆世界杯足球賽趣斤。
提示:這個(gè)方法很簡(jiǎn)單, 但是并沒有提供一個(gè)很好的控制。 因?yàn)閮烧叩奶幚頇C(jī)制不同, 最好不要混用黎休。
-
f.write()函數(shù):將 string 寫入到文件中, 然后返回寫入的字符數(shù)浓领。
f.write(string) 將 string 寫入到文件中, 然后返回寫入的字符數(shù)。
寫入文件势腮,這次采用的權(quán)限模式是: w
表示:打開一個(gè)文件只用于寫入联贩。如果該文件已存在則打開文件,并從開頭開始編輯嫉鲸,即原有內(nèi)容會(huì)被刪除撑蒜。如果該文件不存在,創(chuàng)建新文件玄渗。
運(yùn)行前座菠,文件目錄中是沒有
file_write.txt
文件的,如上圖藤树。
# f.write()
with open('file_write.txt','w+', encoding="utf-8") as f:
count = f.write("Python is an elegant program language")
print(count)
運(yùn)行結(jié)果:(影響3個(gè))
1.文件目錄的變化:創(chuàng)建出了file_write.txt
文件浴滴。
2.在新創(chuàng)建出的file_write.txt
文件中寫入了內(nèi)容:Python is an elegant program language
,如下圖:
3.打印結(jié)果:
37
返回此次寫入的字符數(shù)岁钓。
- 如果要寫入一些不是字符串的東西升略,如元組、字典, 那么將需要先進(jìn)行轉(zhuǎn)換屡限。
例如:
# 寫入元組
with open("file_write_tuple.txt", 'w', encoding='utf-8') as f:
t = (2018, '二0一八年')
count = f.write(str(t))
print(count)
運(yùn)行結(jié)果:(影響3個(gè))
1.1.文件目錄的變化:創(chuàng)建出了file_write_tuple.txt
文件品嚣。
2.在新創(chuàng)建出的
file_write_tuple.txt
文件中寫入了內(nèi)容:(2018, '二0一八年')
3.打印結(jié)果:
15
-
f.tell()函數(shù)
f.tell()函數(shù),返回文件對(duì)象當(dāng)前指針?biāo)幍奈恢? 它是從文件開頭開始算起的字節(jié)數(shù)钧大。
-
f.seek(offset, from_what) 函數(shù)翰撑,
如果要改變文件當(dāng)前的位置, 可以使用 f.seek(offset, from_what) 函數(shù)。
from_what 的值, 如果是 0 表示開頭, 如果是 1 表示當(dāng)前位置, 2 表示文件的結(jié)尾啊央,例如:
seek(x,0) : 從起始位置即文件首行首字符開始移動(dòng) x 個(gè)字符
seek(x,1) : 表示從當(dāng)前位置往后移動(dòng)x個(gè)字符
seek(-x,2):表示從文件的結(jié)尾往前移動(dòng)x個(gè)字符
from_what 值可以省略眶诈,默認(rèn)為0涨醋,即文件開頭。下面給出一個(gè)完整的例子:
# seek()
with open("file_write_bit.txt", 'wb+') as f:
f.write(b'0123456789abcedfgh')
s_3 = f.seek(3)
print(s_3)
print(f.tell())
print(f.read(1))
print('--------------')
s_4 = f.seek(4, 1)
print(s_4)
print(f.tell())
print(f.read(1))
運(yùn)行結(jié)果:
3
3
b'3'
--------------
8
8
b'8'
-
f.close()函數(shù)
在文本文件中 (那些打開文件的模式下沒有 b 的), 只會(huì)相對(duì)于文件起始位置進(jìn)行定位逝撬。
當(dāng)你處理完一個(gè)文件后, 調(diào)用 f.close()
來關(guān)閉文件并釋放系統(tǒng)的資源浴骂,如果嘗試再調(diào)用該文件,則會(huì)拋出異常宪潮。ValueError: I/O operation on closed file.
# 讀取文件:第一種寫法
try:
my_test_file = open("io_test.txt", encoding='utf-8')
content = my_test_file.read()
print(content)
finally:
if my_test_file:
my_test_file.close()
當(dāng)處理一個(gè)文件對(duì)象時(shí), 使用 with 關(guān)鍵字是非常好的方式溯警。在結(jié)束后, 它會(huì)幫你正確的關(guān)閉文件。 而且寫起來也比 try - finally
語(yǔ)句塊要簡(jiǎn)短坎炼。
# 讀取文件:第二種寫法
with open('io_test.txt', 'r') as f:
文件對(duì)象還有其他方法, 如 isatty() 和 trucate(), 但這些通常比較少用愧膀。
pickle 模塊
python的pickle模塊實(shí)現(xiàn)了基本的數(shù)據(jù)序列和反序列化拦键。
通過pickle模塊的序列化操作我們能夠?qū)⒊绦蛑羞\(yùn)行的對(duì)象信息保存到文件中去谣光,永久存儲(chǔ)。
通過pickle模塊的反序列化操作芬为,我們能夠從文件中創(chuàng)建上一次程序保存的對(duì)象萄金。
基本接口:
pickle.dump(obj, file, [,protocol])
有了 pickle 這個(gè)對(duì)象, 就能對(duì) file 以讀取的形式打開:
x = pickle.load(file)
注解:從 file 中讀取一個(gè)字符串,并將它重構(gòu)為原來的python對(duì)象媚朦。
file: 類文件對(duì)象氧敢,有read()和readline()接口。
以Json數(shù)據(jù)結(jié)構(gòu)保存數(shù)據(jù)
我們已經(jīng)可以輕松地將字符串寫入文件并從文件中讀取询张。數(shù)字需要更多的努力孙乖,因?yàn)樵?code>read()方法只返回字符串,必須將其傳遞給類似的函數(shù)int()
份氧,它接受類似字符串'123'
并返回其數(shù)值123.當(dāng)您想要保存更復(fù)雜的數(shù)據(jù)類型(如嵌套列表和字典唯袄,手工解析和序列化變得復(fù)雜)。
Python允許使用稱為Json的流行數(shù)據(jù)交換格式蜗帜,而不是讓用戶不斷編寫和調(diào)試代碼恋拷,以將復(fù)雜的數(shù)據(jù)類型保存到文件中。調(diào)用的標(biāo)準(zhǔn)模塊json厅缺,可以采用Python數(shù)據(jù)層次結(jié)構(gòu)蔬顾,并將它們轉(zhuǎn)換為字符串表示形式,這個(gè)過程稱為序列化湘捎。從字符串表示中重建數(shù)據(jù)稱為反序列化诀豁。在序列化和反序列化之間,表示對(duì)象的字符串可能已存儲(chǔ)在文件或數(shù)據(jù)中窥妇,或通過網(wǎng)絡(luò)連接發(fā)送到某個(gè)遠(yuǎn)程服務(wù)器舷胜。
-
提示
JSON格式通常被現(xiàn)代應(yīng)用程序用于允許數(shù)據(jù)交換的首要選擇。
-
序列化
我們把變量從內(nèi)存中變成可存儲(chǔ)或傳輸?shù)倪^程稱之為序列化秩伞,在Python中叫pickling逞带,在其他語(yǔ)言中也被稱之為serialization欺矫,marshalling,flattening等等展氓,都是一個(gè)意思穆趴。
-
反序列化
序列化之后,就可以把序列化后的內(nèi)容寫入磁盤遇汞,或者通過網(wǎng)絡(luò)傳輸?shù)絼e的機(jī)器上未妹。反過來,把變量?jī)?nèi)容從序列化的對(duì)象重新讀到內(nèi)存里稱之為反序列化空入,即unpickling络它。Python提供了pickle模塊來實(shí)現(xiàn)序列化。
Python內(nèi)置的json模塊提供了非常完善的Python對(duì)象到JSON格式的轉(zhuǎn)換歪赢。我們先看看如何把Python對(duì)象變成一個(gè)JSON化戳。
如果您有一個(gè)對(duì)象x,則可以使用一行簡(jiǎn)單的代碼查看其JSON字符串表示:
import json
# 把對(duì)象轉(zhuǎn)換為Json字符串
t = [2018, "21屆世界杯", '俄羅斯']
# 核心代碼埋凯,使用前要先導(dǎo)入json模塊点楼,此處是json.dumps(t)方法,區(qū)別于方法json.dump(t白对,f)
json_str = json.dumps(t)
print(t)
print(json_str)
運(yùn)行結(jié)果:
[2018, '21屆世界杯', '俄羅斯']
[2018, "21\u5c4a\u4e16\u754c\u676f", "\u4fc4\u7f57\u65af"]
提示:
1.使用前要先導(dǎo)入json
模塊掠廓,此處是json.dumps(t)
方法,區(qū)別于方法json.dump(t甩恼,f)
蟀瞧。如果你遇到了下面錯(cuò)誤,請(qǐng)檢查方法是否使用錯(cuò)誤:
Traceback (most recent call last):
File "F:/python_projects/io_file/my_file.py", line 84, in <module>
json_str = json.dump(t)
TypeError: dump() missing 1 required positional argument: 'fp'
此文中用的python是3.5条摸,在版本3.6中已更改:所有可選參數(shù)現(xiàn)在僅為關(guān)鍵字悦污。
json.dumps(obj,*屈溉,skipkeys = False塞关,ensure_ascii = True,check_circular = True子巾,allow_nan = True帆赢,cls = None,indent = None线梗,separators = None椰于,default = None,sort_keys = False仪搔,** kw )
2.json.dumps(t)方法的另一個(gè)變體
json.dump(t瘾婿,f)```,只是將對(duì)象序列化為文本文件。
dumps()方法返回一個(gè)str偏陪,內(nèi)容就是標(biāo)準(zhǔn)的JSON抢呆。類似的,dump()方法可以直接把JSON寫入一個(gè)file-like Object笛谦。
import json
# 直接把對(duì)象以Json字符串寫入文件
t = [2018, "21屆世界杯", '俄羅斯']
with open('file_write_json.txt','w') as f:
json_str = json.dump(t,f)
要把JSON反序列化為Python對(duì)象抱虐,用loads()
或者對(duì)應(yīng)的load()
方法,前者把JSON的字符串反序列化饥脑,后者從file-like Object
中讀取字符串并反序列化恳邀。
-
用
loads()
反序列化
import json
# 直接把對(duì)象以Json字符串寫入文件
t = [2018, "21屆世界杯", '俄羅斯']
with open('file_write_json.txt','w') as f:
json_str = json.dumps(t)
print(json_str)
# 反序列化
t2 = json.loads(json_str)
print(t2)
運(yùn)行結(jié)果:
[2018, "21\u5c4a\u4e16\u754c\u676f", "\u4fc4\u7f57\u65af"]
[2018, '21屆世界杯', '俄羅斯']
-
用
load()
反序列化
用load()
反序列化,直接將文件反序列化為內(nèi)存中的對(duì)象灶轰。
import json
t = [2018, "21屆世界杯", '俄羅斯']
with open('file_write_json.txt', 'r+') as f:
t3 = json.load(f)
print(t3)
運(yùn)行結(jié)果:
[2018, '21屆世界杯', '俄羅斯']
pickle
與JSON相反谣沸,pickle是一種允許對(duì)任意復(fù)雜Python對(duì)象進(jìn)行序列化的協(xié)議。因此笋颤,它特定于Python乳附,不能用于與其他語(yǔ)言編寫的應(yīng)用程序通信。默認(rèn)情況下它也是不安全的:如果數(shù)據(jù)是由熟練的攻擊者精心設(shè)計(jì)的椰弊,則反序列化來自不受信任來源的pickle數(shù)據(jù)可以執(zhí)行任意代碼许溅。
說白了就是:Pickle的問題和所有其他編程語(yǔ)言特有的序列化問題一樣瓤鼻,就是它只能用于Python秉版,并且可能不同版本的Python彼此都不兼容。
接下來一節(jié)會(huì)單獨(dú)拿出來學(xué)習(xí)序列化茬祷、反序列化清焕。
小結(jié)
本文著重學(xué)習(xí)文件讀寫和涉及到了序列化和反序列化,接下來一節(jié)會(huì)單獨(dú)拿出來學(xué)習(xí)序列化祭犯、反序列化秸妥。
更多了解,可關(guān)注公眾號(hào):人人懂編程