修復頭條螢幕適配方案導致的兩次獲取尺寸不一問題
阿新 • • 發佈:2022-04-19
1 引入
應用程式執行過程中產生的資料最先都是存放於記憶體中的,若想永久儲存下來,必須要保存於硬碟中。應用程式若想操作硬體必須通過作業系統,而檔案就是作業系統提供給應用程式來操作硬碟的虛擬概念,使用者或應用程式對檔案的操作,就是向作業系統發起呼叫,然後由作業系統完成對硬碟的具體操作。
2 檔案操作的基本流程
2.1 基本流程
1 # 1. 開啟檔案,由應用程式向作業系統發起系統呼叫open(...),作業系統開啟該檔案,對應一塊硬碟空間,並返回一個檔案物件賦值給一個變數f 2 f=open('a.txt','r',encoding='utf-8') #預設開啟模式就為r 3 4 # 2. 呼叫檔案物件下的讀/寫方法,會被作業系統轉換為讀/寫硬碟的操作5 data=f.read() 6 7 # 3. 向作業系統發起關閉檔案的請求,回收系統資源 8 f.close()
# 1. 開啟檔案,由應用程式向作業系統發起系統呼叫open(...),作業系統開啟該檔案,對應一塊硬碟空間,並返回一個檔案物件賦值給一個變數f f=open('a.txt','r',encoding='utf-8') #預設開啟模式就為r # 2. 呼叫檔案物件下的讀/寫方法,會被作業系統轉換為讀/寫硬碟的操作 data=f.read() # 3. 向作業系統發起關閉檔案的請求,回收系統資源 f.close()
2.2 資源回收與with上下文管理
開啟一個檔案包含兩部分資源:應用程式的變數f和作業系統開啟的檔案。在操作完畢一個檔案時,必須把與該檔案的這兩部分資源全部回收,回收方法為:
1、f.close() #回收作業系統開啟的檔案資源 2、del f #回收應用程式級的變數
其中del f一定要發生在f.close()之後,否則就會導致作業系統開啟的檔案無法關閉,白白佔用資源, 而python自動的垃圾回收機制決定了我們無需考慮del f,這就要求我們,在操作完畢檔案後,一定要記住f.close(),雖然我們如此強調,但是大多數讀者還是會不由自主地忘記f.close(),考慮到這一點,python提供了with關鍵字來幫我們管理上下文
# 1、在執行完子程式碼塊後,with 會自動執行f.close() with open('a.txt','w') as f: pass # 2、可用用with同時開啟多個檔案,用逗號分隔開即可 with open('a.txt','r') as read_f,open('b.txt','w') as write_f: data = read_f.read() write_f.write(data)
2.3 指定操作文字檔案的字元編碼
f = open(...)
# 是由作業系統開啟檔案,如果開啟的是文字檔案,會涉及到字元編碼問題,
# 如果沒有為open指定編碼,那麼開啟文字檔案的預設編碼很明顯是作業系統說了算了,
# 作業系統會用自己的預設編碼去開啟檔案,在windows下是gbk,在linux下是utf-8。
# 這就用到了上節課講的字元編碼的知識:若要保證不亂碼,檔案以什麼方式存的,
# 就要以什麼方式開啟。
f = open('a.txt','r',encoding='utf-8')
3 檔案的操作模式
3.1 控制檔案讀寫操作的模式
r(預設的):只讀
w:只寫
a:只追加寫
3.1.1 案例一: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('賬號名或者密碼錯誤')
3.1.2 案例二: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模式開啟檔案,則會清空檔案內容
3.1.3 案例三: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)
3.1.4 案例四:+ 模式的使用(瞭解)
# r+ w+ a+ :可讀可寫 #在平時工作中,我們只單純使用r/w/a,要麼只讀,要麼只寫,一般不用可讀可寫的模式
3.2 控制檔案讀寫內容的模式
大前提: tb模式均不能單獨使用,必須與r/w/a之一結合使用
t(預設的):文字模式
1. 讀寫檔案都是以字串為單位的
2. 只能針對文字檔案
3. 必須指定encoding引數
b:二進位制模式:
1.讀寫檔案都是以bytes/二進位制為單位的
2. 可以針對所有檔案
3. 一定不能指定encoding引數
3.2.1 案例一: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 模式時,內部幫我們做了編碼與解碼。
3.2.2 案例二: 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)
4 操作檔案的方法
4.1 重點
# 讀操作 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模式
4.2 瞭解
f.readable() # 檔案是否可讀 f.writable() # 檔案是否可讀 f.closed # 檔案是否關閉 f.encoding # 如果檔案開啟模式為b,則沒有該屬性 f.flush() # 立刻將檔案內容從記憶體刷到硬碟 f.name
5 主動控制檔案內指標移動
#大前提:檔案內指標的移動都是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模式下用
5.1 案例一: 0模式詳解
# 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')) #輸出結果為: 好
5.2 案例二: 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
5.3 案例三: 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')) # 輸出結果為:好 # 小練習:實現動態檢視最新一條日誌的效果 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='')
6 檔案的修改
# 檔案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、記憶體中的資料是可以修改的
檔案對應的是硬碟空間,硬碟不能修改對應著檔案本質也不能修改, 那我們看到檔案的內容可以修改,是如何實現的呢? 大致的思路是將硬碟中檔案內容讀入記憶體,然後在記憶體中修改完畢後再覆蓋回硬碟 具體的實現方式分為兩種:
6.1 檔案修改方式一
# 實現思路:將檔案內容發一次性全部讀入記憶體,然後在記憶體中修改完畢後再覆蓋寫回原檔案 # 優點: 在檔案修改過程中同一份資料只有一份 # 缺點: 會過多地佔用記憶體 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('kevin','SB'))
6.1 檔案修改方式二
# 實現思路:以讀的方式開啟原檔案,以寫的方式開啟一個臨時檔案,一行行讀取原檔案內容,修改完後寫入臨時檔案...,刪掉原檔案,將臨時檔案重新命名原檔名 # 優點: 不會佔用過多的記憶體 # 缺點: 在檔案修改過程中同一份資料存了兩份 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')