網(wǎng)站首頁 編程語言 正文
01、文件操作
文件是操作系統(tǒng)提供給用戶/應(yīng)用程序操作硬盤的一個(gè)虛擬的概念/接口
用戶/應(yīng)用程序可以通過文件將數(shù)據(jù)永久保存在硬盤中
用戶/應(yīng)用程序直接操作的是文件,對(duì)文件進(jìn)行的所有的操作,都是在向操作系統(tǒng)發(fā)送系統(tǒng)調(diào)用,然后再由操作系統(tǒng)將其轉(zhuǎn)成具體的硬盤操作
1.1、文件操作流程
打開文件:
打開文件,由應(yīng)用系統(tǒng)向操作系統(tǒng)發(fā)起系統(tǒng)調(diào)用open(),操作系統(tǒng)打開該文件,對(duì)應(yīng)一塊硬盤空間,并返回一個(gè)文件對(duì)象賦值給一個(gè)變量f
"""Windows路徑分隔符問題"""
"""方案1 (推薦)"""
f=open(r'C:\a\b\c\d.txt',mode='rt')#r原生字符串rawstring
"""方案2"""
f=open(r'C:/a/b/c/d.txt')
操作文件(讀/寫):
調(diào)用文件對(duì)象下的讀/寫方法,會(huì)被操作系統(tǒng)轉(zhuǎn)換為讀/寫硬盤的操作
f.read()
關(guān)閉文件:
向操作系統(tǒng)發(fā)起關(guān)閉文件的請(qǐng)求,回收系統(tǒng)資源
f.close()
with上下文管理:
#1. 執(zhí)行完代碼后,with會(huì)自動(dòng)執(zhí)行fp.close()
with open('a.txt',mode='rt')as fp:
res=fp.read()#t模式會(huì)將fp.read()讀出來的結(jié)果解碼成unicode
print(res)
#2. 用with打開多個(gè)文件,用逗號(hào)分割開即可
with open('a.txt','r') as f1,open('b.txt','r')as f2:
res1=f1.read()
res2=f2.read()
print(res1)
print(res2)
指定操作文件的字符編碼
"""
f=open(....)由操作系統(tǒng)打開文件,如果打開的是文本文件,會(huì)涉及到字符編碼問題,如果沒有為open指定編碼,那么打開文本文件的默認(rèn)編碼是操作系統(tǒng)說了算的,
操作系統(tǒng)會(huì)用自己默認(rèn)的編碼去打開文件,在windos下是gbk,在Linux下是utf-8
"""
1.2、文件的操作模式
控制文件讀寫操作的模式
"""
r(默認(rèn)的)只讀
w:只寫
a:只追加寫
"""
r模式使用:
# r只讀模式: 在文件不存在時(shí)則報(bào)錯(cuò),文件存在文件內(nèi)指針直接跳到文件開頭
with open('a.txt',mode='r',encoding='utf-8') as f:
res=f.read() # 會(huì)將文件的內(nèi)容由硬盤全部讀入內(nèi)存,賦值給res
# 小練習(xí):實(shí)現(xiàn)用戶認(rèn)證功能
inp_name=input('請(qǐng)輸入你的名字: ').strip()
inp_pwd=input('請(qǐng)輸入你的密碼: ').strip()
with open(r'db.txt',mode='r',encoding='utf-8') as f:
for line in f:
# 把用戶輸入的名字與密碼與讀出內(nèi)容做比對(duì)
u,p=line.strip('\n').split(':')
if inp_name == u and inp_pwd == p:
print('登錄成功')
break
else:
print('賬號(hào)名或者密碼錯(cuò)誤')
w模式的使用:
# w只寫模式: 在文件不存在時(shí)會(huì)創(chuàng)建空文檔,文件存在會(huì)清空文件,文件指針跑到文件開頭
with open('b.txt',mode='w',encoding='utf-8') as f:
f.write('你好\n')
f.write('我好\n')
f.write('大家好\n')
f.write('111\n222\n333\n')
#強(qiáng)調(diào):
# 1 在文件不關(guān)閉的情況下,連續(xù)的寫入,后寫的內(nèi)容一定跟在前寫內(nèi)容的后面
# 2 如果重新以w模式打開文件,則會(huì)清空文件內(nèi)容
#案例:w模式用來創(chuàng)建全新的文件
#文件的copy工具
src_file=input("源文件路徑》》").strip()
dsc_file=input("源文件路徑》》").strip()
with open(r'{}'.format(src_file),mode='rt',encoding='utf-8')as f1,open(r'{}'.format(dsc_file),mode='wt',encoding='utf-8')as f2:
res=f1.read()
f2.write(res)
a模式的使用:
# a只追加寫模式: 在文件不存在時(shí)會(huì)創(chuàng)建空文檔,文件存在會(huì)將文件指針直接移動(dòng)到文件末尾
with open('c.txt',mode='a',encoding='utf-8') as f:
f.write('44444\n')
f.write('55555\n')
#強(qiáng)調(diào) w 模式與 a 模式的異同:
# 1 相同點(diǎn):在打開的文件不關(guān)閉的情況下,連續(xù)的寫入,新寫的內(nèi)容總會(huì)跟在前寫的內(nèi)容之后
# 2 不同點(diǎn):以 a 模式重新打開文件,不會(huì)清空原文件內(nèi)容,會(huì)將文件指針直接移動(dòng)到文件末尾,新寫的內(nèi)容永遠(yuǎn)寫在最后
# 小練習(xí):實(shí)現(xiàn)注冊(cè)功能:把用戶名和密碼添加至數(shù)據(jù)庫
name=input('username>>>: ').strip()
pwd=input('password>>>: ').strip()
with open('db1.txt',mode='a',encoding='utf-8') as f:
info='%s:%s\n' %(name,pwd)
f.write(info)
+模式的使用
# r+ w+ a+ :可讀可寫
#在平時(shí)工作中,我們只單純使用r/w/a,要么只讀,要么只寫,一般不用可讀可寫的模式
控制文件讀寫內(nèi)容的模式
大前提: tb模式均不能單獨(dú)使用,必須與r/w/a之一結(jié)合使用
t(默認(rèn)的):文本模式
- ? ? 1. 讀寫文件都是以字符串為單位的
- ? ? 2. 只能針對(duì)文本文件
- ? ? 3. 必須指定encoding參數(shù)
b:二進(jìn)制模式:
- ? ?1.讀寫文件都是以bytes/二進(jìn)制為單位的
- ? ?2. 可以針對(duì)所有文件
- ? ?3. 一定不能指定encoding參數(shù)
t模式的使用:
# t 模式:如果我們指定的文件打開模式為r/w/a,其實(shí)默認(rèn)就是rt/wt/at
with open('a.txt',mode='rt',encoding='utf-8') as f:
res=f.read()
print(type(res)) # 輸出結(jié)果為:<class 'str'>
with open('a.txt',mode='wt',encoding='utf-8') as f:
s='abc'
f.write(s) # 寫入的也必須是字符串類型
#強(qiáng)調(diào):t 模式只能用于操作文本文件,無論讀寫,都應(yīng)該以字符串為單位,而存取硬盤本質(zhì)都是二進(jìn)制的形式,當(dāng)指定 t 模式時(shí),內(nèi)部幫我們做了編碼與解碼
b模式的使用:
# b: 讀寫都是以二進(jìn)制位單位
with open('1.mp4',mode='rb') as f:
data=f.read()
print(type(data)) # 輸出結(jié)果為:<class 'bytes'>
with open('a.txt',mode='wb') as f:
msg="你好"
res=msg.encode('utf-8') # res為bytes類型
f.write(res) # 在b模式下寫入文件的只能是bytes類型
#強(qiáng)調(diào):b模式對(duì)比t模式
1、在操作純文本文件方面t模式幫我們省去了編碼與解碼的環(huán)節(jié),b模式則需要手動(dòng)編碼與解碼,所以此時(shí)t模式更為方便
2、針對(duì)非文本文件(如圖片、視頻、音頻等)只能使用b模式
# 小練習(xí): 編寫拷貝工具
src_file=input('源文件路徑: ').strip()
dst_file=input('目標(biāo)文件路徑: ').strip()
with open(r'%s' %src_file,mode='rb') as read_f,open(r'%s' %dst_file,mode='wb') as write_f:
for line in read_f:
# print(line)
write_f.write(line)
1.3、操作文件的方法
重點(diǎn):
# 讀操作
f.read() # 讀取所有內(nèi)容,執(zhí)行完該操作后,文件指針會(huì)移動(dòng)到文件末尾
f.readline() # 讀取一行內(nèi)容,光標(biāo)移動(dòng)到第二行首部
f.readlines() # 讀取每一行內(nèi)容,存放于列表中
# 強(qiáng)調(diào):
# f.read()與f.readlines()都是將內(nèi)容一次性讀入內(nèi)容,如果內(nèi)容過大會(huì)導(dǎo)致內(nèi)存溢出,若還想將內(nèi)容全讀入內(nèi)存,則必須分多次讀入,有兩種實(shí)現(xiàn)方式:
# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
for line in f:
print(line) # 同一時(shí)刻只讀入一行內(nèi)容到內(nèi)存中
# 方式二
with open('1.mp4',mode='rb') as f:
while True:
data=f.read(1024) # 同一時(shí)刻只讀入1024個(gè)Bytes到內(nèi)存中
if len(data) == 0:
break
print(data)
# 寫操作
f.write('1111\n222\n') # 針對(duì)文本模式的寫,需要自己寫換行符
f.write('1111\n222\n'.encode('utf-8')) # 針對(duì)b模式的寫,需要自己寫換行符
f.writelines(['333\n','444\n']) # 文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式
了解:
f.readable() # 文件是否可讀
f.writable() # 文件是否可讀
f.closed # 文件是否關(guān)閉
f.encoding # 如果文件打開模式為b,則沒有該屬性
f.flush() # 立刻將文件內(nèi)容從內(nèi)存刷到硬盤
f.name
1.4、主動(dòng)移動(dòng)文件內(nèi)指針移動(dòng)
#大前提:文件內(nèi)指針的移動(dòng)都是Bytes為單位的,唯一例外的是t模式下的read(n),n以字符為單位
with open('a.txt',mode='rt',encoding='utf-8') as f:
data=f.read(3) # 讀取3個(gè)字符
with open('a.txt',mode='rb') as f:
data=f.read(3) # 讀取3個(gè)Bytes
# 之前文件內(nèi)指針的移動(dòng)都是由讀/寫操作而被動(dòng)觸發(fā)的,若想讀取文件某一特定位置的數(shù)據(jù),則則需要用f.seek方法主動(dòng)控制文件內(nèi)指針的移動(dòng),詳細(xì)用法如下:
# f.seek(指針移動(dòng)的字節(jié)數(shù),模式控制):
# 模式控制:
# 0: 默認(rèn)的模式,該模式代表指針移動(dòng)的字節(jié)數(shù)是以文件開頭為參照的
# 1: 該模式代表指針移動(dòng)的字節(jié)數(shù)是以當(dāng)前所在的位置為參照的
# 2: 該模式代表指針移動(dòng)的字節(jié)數(shù)是以文件末尾的位置為參照的
# 強(qiáng)調(diào):其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用
0模式:
只有0模式可以在t下使用,1,2必須在b模式下使用
# a.txt用utf-8編碼,內(nèi)容如下(abc各占1個(gè)字節(jié),中文“你好”各占3個(gè)字節(jié))
abc你好
# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
f.seek(3,0) # 參照文件開頭移動(dòng)了3個(gè)字節(jié)
print(f.tell()) # 查看當(dāng)前文件指針距離文件開頭的位置,輸出結(jié)果為3
print(f.read()) # 從第3個(gè)字節(jié)的位置讀到文件末尾,輸出結(jié)果為:你好
# 注意:由于在t模式下,會(huì)將讀取的內(nèi)容自動(dòng)解碼,所以必須保證讀取的內(nèi)容是一個(gè)完整中文數(shù)據(jù),否則解碼失敗
with open('a.txt',mode='rb') as f:
f.seek(6,0)
print(f.read().decode('utf-8')) #輸出結(jié)果為: 好
1模式:
# 1模式的使用
with open('a.txt',mode='rb') as f:
f.seek(3,1) # 從當(dāng)前位置往后移動(dòng)3個(gè)字節(jié),而此時(shí)的當(dāng)前位置就是文件開頭
print(f.tell()) # 輸出結(jié)果為:3
f.seek(4,1) # 從當(dāng)前位置往后移動(dòng)4個(gè)字節(jié),而此時(shí)的當(dāng)前位置為3
print(f.tell()) # 輸出結(jié)果為:7
2模式:
# a.txt用utf-8編碼,內(nèi)容如下(abc各占1個(gè)字節(jié),中文“你好”各占3個(gè)字節(jié))
abc你好
# 2模式的使用
with open('a.txt',mode='rb') as f:
f.seek(0,2) # 參照文件末尾移動(dòng)0個(gè)字節(jié), 即直接跳到文件末尾
print(f.tell()) # 輸出結(jié)果為:9
f.seek(-3,2) # 參照文件末尾往前移動(dòng)了3個(gè)字節(jié)
print(f.read().decode('utf-8')) # 輸出結(jié)果為:好
# 小練習(xí):實(shí)現(xiàn)動(dòng)態(tài)查看最新一條日志的效果
#exe.py:
with open('access.log','a',encoding='utf-8')as fp:
fp.write('202120202020200 收款2000元\n')
#access_log.py:
import time
with open('access.log',mode='rb') as f:
f.seek(0,2)#將指針移到末尾
while True:
line=f.readline()
if len(line) == 0:
# 沒有內(nèi)容
time.sleep(0.5)
else:
print(line.decode('utf-8'),end='')
"""每執(zhí)行一次exe.py,access_log就會(huì)檢測(cè)到數(shù)據(jù),并打印出來"""
1.5文件的修改
# 文件a.txt內(nèi)容如下
張一蛋 山東 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 執(zhí)行操作
with open('a.txt',mode='r+t',encoding='utf-8') as f:
f.seek(9)
f.write('<婦女主任>')
# 文件修改后的內(nèi)容如下
張一蛋<婦女主任> 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 強(qiáng)調(diào):
# 1、硬盤空間是無法修改的,硬盤中數(shù)據(jù)的更新都是用新內(nèi)容覆蓋舊內(nèi)容
# 2、內(nèi)存中的數(shù)據(jù)是可以修改的
文件對(duì)應(yīng)的是硬盤空間,硬盤不能修改對(duì)應(yīng)著文件本質(zhì)也不能修改, 那我們看到文件的內(nèi)容可以修改,是如何實(shí)現(xiàn)的呢? 大致的思路是將硬盤中文件內(nèi)容讀入內(nèi)存,然后在內(nèi)存中修改完畢后再覆蓋回硬盤
具體的實(shí)現(xiàn)方式分為兩種:
方式一:
# 實(shí)現(xiàn)思路:將文件內(nèi)容發(fā)一次性全部讀入內(nèi)存,然后在內(nèi)存中修改完畢后再覆蓋寫回原文件
# 優(yōu)點(diǎn): 在文件修改過程中同一份數(shù)據(jù)只有一份
# 缺點(diǎn): 會(huì)過多地占用內(nèi)存
with open('db.txt',mode='rt',encoding='utf-8') as f:
data=f.read()
with open('db.txt',mode='wt',encoding='utf-8') as f:
f.write(data.replace('zhao','SB'))
方式二:
# 實(shí)現(xiàn)思路:以讀的方式打開原文件,以寫的方式打開一個(gè)臨時(shí)文件,一行行讀取原文件內(nèi)容,修改完后寫入臨時(shí)文件...,刪掉原文件,將臨時(shí)文件重命名原文件名
# 優(yōu)點(diǎn): 不會(huì)占用過多的內(nèi)存
# 缺點(diǎn): 在文件修改過程中同一份數(shù)據(jù)存了兩份
import os
with open('db.txt',mode='rt',encoding='utf-8') as read_f,\
open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f:
for line in read_f:
wrife_f.write(line.replace('SB','kevin'))
os.remove('db.txt')
os.rename('.db.txt.swap','db.txt')
1.6垃圾回收機(jī)制
解釋器在執(zhí)行到定義變量的語法時(shí),會(huì)申請(qǐng)內(nèi)存空間來存放變量的值,而內(nèi)存的容量是有限的,這就涉及到變量值所占用內(nèi)存空間的回收問題,當(dāng)一個(gè)變量值沒有用了(簡(jiǎn)稱垃圾)就應(yīng)該將其占用的內(nèi)存給回收掉,那什么樣的變量值是沒有用的呢?
單從邏輯層面分析,我們定義變量將變量值存起來的目的是為了以后取出來使用,而取得變量值需要通過其綁定的直接引用(如x=10,10被x直接引用)或間接引用(如l=[x,],x=10,10被x直接引用,而被容器類型l間接引用),所以當(dāng)一個(gè)變量值不再綁定任何引用時(shí),我們就無法再訪問到該變量值了,該變量值自然就是沒有用的,就應(yīng)該被當(dāng)成一個(gè)垃圾回收。
毫無疑問,內(nèi)存空間的申請(qǐng)與回收都是非常耗費(fèi)精力的事情,而且存在很大的危險(xiǎn)性,稍有不慎就有可能引發(fā)內(nèi)存溢出問題,好在Cpython解釋器提供了自動(dòng)的垃圾回收機(jī)制來幫我們解決了這件事。
什么是GC:
垃圾回收機(jī)制(簡(jiǎn)稱GC)是Python解釋器自帶一種機(jī),專門用來回收不可用的變量值所占用的內(nèi)存空間
為什么用垃圾回收機(jī)制:
程序運(yùn)行過程中會(huì)申請(qǐng)大量的內(nèi)存空間,而對(duì)于一些無用的內(nèi)存空間如果不及時(shí)清理的話會(huì)導(dǎo)致內(nèi)存使用殆盡(內(nèi)存溢出),導(dǎo)致程序崩潰,因此管理內(nèi)存是一件重要且繁雜的事情,而python解釋器自帶的垃圾回收機(jī)制把程序員從繁雜的內(nèi)存管理中解放出來。
垃圾回收機(jī)制原理分析:
Python的GC模塊主要運(yùn)用了“引用計(jì)數(shù)”(reference counting)來跟蹤和回收垃圾。在引用計(jì)數(shù)的基礎(chǔ)上,還可以通過“標(biāo)記-清除”(mark and sweep)解決容器對(duì)象可能產(chǎn)生的循環(huán)引用的問題,并且通過“分代回收”(generation collection)以空間換取時(shí)間的方式來進(jìn)一步提高垃圾回收的效率。
(一)引用計(jì)數(shù)
引用計(jì)數(shù)就是:變量值被變量名關(guān)聯(lián)的次數(shù)
如:age=18
變量值18被關(guān)聯(lián)了一個(gè)變量名age,稱之為引用計(jì)數(shù)為1
引用計(jì)數(shù)增加:
age=18 (此時(shí),變量值18的引用計(jì)數(shù)為1)
m=age (把a(bǔ)ge的內(nèi)存地址給了m,此時(shí),m,age都關(guān)聯(lián)了18,所以變量值18的引用計(jì)數(shù)為2)
引用計(jì)數(shù)減少:
age=10(名字age先與值18解除關(guān)聯(lián),再與3建立了關(guān)聯(lián),變量值18的引用計(jì)數(shù)為1)
del m(del的意思是解除變量名x與變量值18的關(guān)聯(lián)關(guān)系,此時(shí),變量18的引用計(jì)數(shù)為0)
值18的引用計(jì)數(shù)一旦變?yōu)?,其占用的內(nèi)存地址就應(yīng)該被解釋器的垃圾回收機(jī)制回收
a=12 #直接引用
b=a #間接引用
"""循環(huán)引用"""
l1=[111,]
l2=[222,]
l1.append(l2)#l1=[值111的內(nèi)存地址,l2列表的內(nèi)存地址]
l2.append(l1)#l2=[值222的內(nèi)存地址,l1列表的內(nèi)存地址]
print(id(l1[1]))
print(id(l2))
print(id(l2[1]))
print(id(l1))
print(l2)
print(l1[1])
"""結(jié)果如下"""
2006853601024
2006853601024
2006561499520
2006561499520
[222, [111, [...]]]
[222, [111, [...]]]
原文鏈接:https://blog.csdn.net/ZhaoSong_/article/details/126669400
相關(guān)推薦
- 2023-03-01 Shell腳本read用法實(shí)現(xiàn)_linux shell
- 2022-12-29 React中的Hooks路由跳轉(zhuǎn)問題_React
- 2022-11-11 Navicat?Premium自定義?sql?標(biāo)簽的創(chuàng)建方式_數(shù)據(jù)庫其它
- 2022-08-01 iOS?UITextView?實(shí)現(xiàn)類似微博的話題、提及用戶效果_IOS
- 2022-08-31 C語言實(shí)現(xiàn)交換排序算法(冒泡,快速排序)的示例代碼_C 語言
- 2022-06-29 Python?高級(jí)教程之線程進(jìn)程和協(xié)程的代碼解析_python
- 2022-11-22 python枚舉類型定義與使用講解_python
- 2022-04-05 MAC中執(zhí)行.sh腳本,/bin/sh^M: bad interpreter:解決辦法
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支