日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Python文件處理與垃圾回收機制詳情_python

作者:過期的秋刀魚-_ ? 更新時間: 2022-11-01 編程語言

01、文件操作

文件是操作系統提供給用戶/應用程序操作硬盤的一個虛擬的概念/接口

用戶/應用程序可以通過文件將數據永久保存在硬盤中

用戶/應用程序直接操作的是文件,對文件進行的所有的操作,都是在向操作系統發送系統調用,然后再由操作系統將其轉成具體的硬盤操作

1.1、文件操作流程

打開文件:

打開文件,由應用系統向操作系統發起系統調用open(),操作系統打開該文件,對應一塊硬盤空間,并返回一個文件對象賦值給一個變量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')

操作文件(讀/寫):

調用文件對象下的讀/寫方法,會被操作系統轉換為讀/寫硬盤的操作

f.read()

關閉文件:

向操作系統發起關閉文件的請求,回收系統資源

f.close()

with上下文管理:

#1. 執行完代碼后,with會自動執行fp.close()
with open('a.txt',mode='rt')as fp:
    	res=fp.read()#t模式會將fp.read()讀出來的結果解碼成unicode
        print(res)
#2. 用with打開多個文件,用逗號分割開即可
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(....)由操作系統打開文件,如果打開的是文本文件,會涉及到字符編碼問題,如果沒有為open指定編碼,那么打開文本文件的默認編碼是操作系統說了算的,
操作系統會用自己默認的編碼去打開文件,在windos下是gbk,在Linux下是utf-8
"""

1.2、文件的操作模式

控制文件讀寫操作的模式

"""
	r(默認的)只讀
	w:只寫
	a:只追加寫
"""

r模式使用:

# r只讀模式: 在文件不存在時則報錯,文件存在文件內指針直接跳到文件開頭
 with open('a.txt',mode='r',encoding='utf-8') as f:
     res=f.read() # 會將文件的內容由硬盤全部讀入內存,賦值給res

# 小練習:實現用戶認證功能
 inp_name=input('請輸入你的名字: ').strip()
 inp_pwd=input('請輸入你的密碼: ').strip()
 with open(r'db.txt',mode='r',encoding='utf-8') as f:
     for line in f:
         # 把用戶輸入的名字與密碼與讀出內容做比對
         u,p=line.strip('\n').split(':')
         if inp_name == u and inp_pwd == p:
             print('登錄成功')
             break
     else:
         print('賬號名或者密碼錯誤')

w模式的使用:

# w只寫模式: 在文件不存在時會創建空文檔,文件存在會清空文件,文件指針跑到文件開頭
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')
#強調:
# 1 在文件不關閉的情況下,連續的寫入,后寫的內容一定跟在前寫內容的后面
# 2 如果重新以w模式打開文件,則會清空文件內容

#案例:w模式用來創建全新的文件
#文件的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只追加寫模式: 在文件不存在時會創建空文檔,文件存在會將文件指針直接移動到文件末尾
 with open('c.txt',mode='a',encoding='utf-8') as f:
     f.write('44444\n')
     f.write('55555\n')
#強調 w 模式與 a 模式的異同:
# 1 相同點:在打開的文件不關閉的情況下,連續的寫入,新寫的內容總會跟在前寫的內容之后
# 2 不同點:以 a 模式重新打開文件,不會清空原文件內容,會將文件指針直接移動到文件末尾,新寫的內容永遠寫在最后

# 小練習:實現注冊功能:把用戶名和密碼添加至數據庫
 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+ :可讀可寫
#在平時工作中,我們只單純使用r/w/a,要么只讀,要么只寫,一般不用可讀可寫的模式

控制文件讀寫內容的模式

大前提: tb模式均不能單獨使用,必須與r/w/a之一結合使用
t(默認的):文本模式

  • ? ? 1. 讀寫文件都是以字符串為單位的
  • ? ? 2. 只能針對文本文件
  • ? ? 3. 必須指定encoding參數

b:二進制模式:

  • ? ?1.讀寫文件都是以bytes/二進制為單位的
  • ? ?2. 可以針對所有文件
  • ? ?3. 一定不能指定encoding參數

t模式的使用:

# t 模式:如果我們指定的文件打開模式為r/w/a,其實默認就是rt/wt/at
 with open('a.txt',mode='rt',encoding='utf-8') as f:
     res=f.read() 
     print(type(res)) # 輸出結果為:<class 'str'>

 with open('a.txt',mode='wt',encoding='utf-8') as f:
     s='abc'
     f.write(s) # 寫入的也必須是字符串類型

 #強調:t 模式只能用于操作文本文件,無論讀寫,都應該以字符串為單位,而存取硬盤本質都是二進制的形式,當指定 t 模式時,內部幫我們做了編碼與解碼

b模式的使用:

# b: 讀寫都是以二進制位單位
 with open('1.mp4',mode='rb') as f:
     data=f.read()
     print(type(data)) # 輸出結果為:<class 'bytes'>

 with open('a.txt',mode='wb') as f:
     msg="你好"
     res=msg.encode('utf-8') # res為bytes類型
     f.write(res) # 在b模式下寫入文件的只能是bytes類型

#強調:b模式對比t模式
1、在操作純文本文件方面t模式幫我們省去了編碼與解碼的環節,b模式則需要手動編碼與解碼,所以此時t模式更為方便
2、針對非文本文件(如圖片、視頻、音頻等)只能使用b模式

# 小練習: 編寫拷貝工具
src_file=input('源文件路徑: ').strip()
dst_file=input('目標文件路徑: ').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、操作文件的方法

重點:

# 讀操作
f.read()  # 讀取所有內容,執行完該操作后,文件指針會移動到文件末尾
f.readline()  # 讀取一行內容,光標移動到第二行首部
f.readlines()  # 讀取每一行內容,存放于列表中

# 強調:
# f.read()與f.readlines()都是將內容一次性讀入內容,如果內容過大會導致內存溢出,若還想將內容全讀入內存,則必須分多次讀入,有兩種實現方式:
# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        print(line) # 同一時刻只讀入一行內容到內存中

# 方式二
with open('1.mp4',mode='rb') as f:
    while True:
        data=f.read(1024) # 同一時刻只讀入1024個Bytes到內存中
        if len(data) == 0:
            break
        print(data)

# 寫操作
f.write('1111\n222\n')  # 針對文本模式的寫,需要自己寫換行符
f.write('1111\n222\n'.encode('utf-8'))  # 針對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  # 文件是否關閉
f.encoding  # 如果文件打開模式為b,則沒有該屬性
f.flush()  # 立刻將文件內容從內存刷到硬盤
f.name

1.4、主動移動文件內指針移動

#大前提:文件內指針的移動都是Bytes為單位的,唯一例外的是t模式下的read(n),n以字符為單位
with open('a.txt',mode='rt',encoding='utf-8') as f:
     data=f.read(3) # 讀取3個字符


with open('a.txt',mode='rb') as f:
     data=f.read(3) # 讀取3個Bytes


# 之前文件內指針的移動都是由讀/寫操作而被動觸發的,若想讀取文件某一特定位置的數據,則則需要用f.seek方法主動控制文件內指針的移動,詳細用法如下:
# f.seek(指針移動的字節數,模式控制): 
# 模式控制:
# 0: 默認的模式,該模式代表指針移動的字節數是以文件開頭為參照的
# 1: 該模式代表指針移動的字節數是以當前所在的位置為參照的
# 2: 該模式代表指針移動的字節數是以文件末尾的位置為參照的
# 強調:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用

0模式:

只有0模式可以在t下使用,1,2必須在b模式下使用

# a.txt用utf-8編碼,內容如下(abc各占1個字節,中文“你好”各占3個字節)
abc你好

# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 參照文件開頭移動了3個字節
    print(f.tell()) # 查看當前文件指針距離文件開頭的位置,輸出結果為3
    print(f.read()) # 從第3個字節的位置讀到文件末尾,輸出結果為:你好
    # 注意:由于在t模式下,會將讀取的內容自動解碼,所以必須保證讀取的內容是一個完整中文數據,否則解碼失敗

with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #輸出結果為: 好

1模式:

# 1模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 從當前位置往后移動3個字節,而此時的當前位置就是文件開頭
    print(f.tell()) # 輸出結果為:3
    f.seek(4,1)     # 從當前位置往后移動4個字節,而此時的當前位置為3
    print(f.tell()) # 輸出結果為:7

2模式:

# a.txt用utf-8編碼,內容如下(abc各占1個字節,中文“你好”各占3個字節)
abc你好

# 2模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 參照文件末尾移動0個字節, 即直接跳到文件末尾
    print(f.tell()) # 輸出結果為:9
    f.seek(-3,2)     # 參照文件末尾往前移動了3個字節
    print(f.read().decode('utf-8')) # 輸出結果為:好

# 小練習:實現動態查看最新一條日志的效果
#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:
                # 沒有內容
                time.sleep(0.5)
            else:
                print(line.decode('utf-8'),end='')
 """每執行一次exe.py,access_log就會檢測到數據,并打印出來"""

1.5文件的修改

# 文件a.txt內容如下
張一蛋     山東    179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422

# 執行操作
with open('a.txt',mode='r+t',encoding='utf-8') as f:
    f.seek(9)
    f.write('<婦女主任>')

# 文件修改后的內容如下
張一蛋<婦女主任> 179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422

# 強調:
# 1、硬盤空間是無法修改的,硬盤中數據的更新都是用新內容覆蓋舊內容
# 2、內存中的數據是可以修改的

文件對應的是硬盤空間,硬盤不能修改對應著文件本質也不能修改, 那我們看到文件的內容可以修改,是如何實現的呢? 大致的思路是將硬盤中文件內容讀入內存,然后在內存中修改完畢后再覆蓋回硬盤

具體的實現方式分為兩種:

方式一:

# 實現思路:將文件內容發一次性全部讀入內存,然后在內存中修改完畢后再覆蓋寫回原文件
# 優點: 在文件修改過程中同一份數據只有一份
# 缺點: 會過多地占用內存
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'))

方式二:

# 實現思路:以讀的方式打開原文件,以寫的方式打開一個臨時文件,一行行讀取原文件內容,修改完后寫入臨時文件...,刪掉原文件,將臨時文件重命名原文件名
# 優點: 不會占用過多的內存
# 缺點: 在文件修改過程中同一份數據存了兩份
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垃圾回收機制

解釋器在執行到定義變量的語法時,會申請內存空間來存放變量的值,而內存的容量是有限的,這就涉及到變量值所占用內存空間的回收問題,當一個變量值沒有用了(簡稱垃圾)就應該將其占用的內存給回收掉,那什么樣的變量值是沒有用的呢?

單從邏輯層面分析,我們定義變量將變量值存起來的目的是為了以后取出來使用,而取得變量值需要通過其綁定的直接引用(如x=10,10被x直接引用)或間接引用(如l=[x,],x=10,10被x直接引用,而被容器類型l間接引用),所以當一個變量值不再綁定任何引用時,我們就無法再訪問到該變量值了,該變量值自然就是沒有用的,就應該被當成一個垃圾回收。

毫無疑問,內存空間的申請與回收都是非常耗費精力的事情,而且存在很大的危險性,稍有不慎就有可能引發內存溢出問題,好在Cpython解釋器提供了自動的垃圾回收機制來幫我們解決了這件事。

什么是GC:

垃圾回收機制(簡稱GC)是Python解釋器自帶一種機,專門用來回收不可用的變量值所占用的內存空間

為什么用垃圾回收機制:

程序運行過程中會申請大量的內存空間,而對于一些無用的內存空間如果不及時清理的話會導致內存使用殆盡(內存溢出),導致程序崩潰,因此管理內存是一件重要且繁雜的事情,而python解釋器自帶的垃圾回收機制把程序員從繁雜的內存管理中解放出來。

垃圾回收機制原理分析:

Python的GC模塊主要運用了“引用計數”(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,還可以通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環引用的問題,并且通過“分代回收”(generation collection)以空間換取時間的方式來進一步提高垃圾回收的效率。

(一)引用計數

引用計數就是:變量值被變量名關聯的次數

如:age=18

變量值18被關聯了一個變量名age,稱之為引用計數為1

引用計數增加:

age=18 (此時,變量值18的引用計數為1)

m=age (把age的內存地址給了m,此時,m,age都關聯了18,所以變量值18的引用計數為2)

引用計數減少:

age=10(名字age先與值18解除關聯,再與3建立了關聯,變量值18的引用計數為1)

del m(del的意思是解除變量名x與變量值18的關聯關系,此時,變量18的引用計數為0)

值18的引用計數一旦變為0,其占用的內存地址就應該被解釋器的垃圾回收機制回收

a=12  #直接引用
b=a   #間接引用
"""循環引用"""
l1=[111,]
l2=[222,]
l1.append(l2)#l1=[值111的內存地址,l2列表的內存地址]
l2.append(l1)#l2=[值222的內存地址,l1列表的內存地址]

print(id(l1[1]))
print(id(l2))

print(id(l2[1]))
print(id(l1))

print(l2)
print(l1[1])

"""結果如下"""
2006853601024
2006853601024

2006561499520
2006561499520

[222, [111, [...]]]
[222, [111, [...]]]

原文鏈接:https://blog.csdn.net/ZhaoSong_/article/details/126669400

欄目分類
最近更新