【STM32H7教程】第82章 STM32H7的QSPI匯流排應用之QSPI Flash執行程式(BOOT+APP方式)
檔案操作
1. 學前三問
1.1 什麼是檔案 ?
檔案是作業系統提供給使用者/應用程式操作硬碟的一種虛擬的概念/介面。
使用者/應用程式
作業系統(檔案) 作業系統會把對使用者/應用程式檔案功能的呼叫轉成具體的硬碟操作
計算機硬體(硬碟)
1.2 為何要用檔案
使用者/應用程式可以通過檔案將資料永久儲存的硬碟中,即操作檔案就是操作硬碟。
使用者/應用程式直接操作的是檔案,對檔案進行的所有操作,都是在向作業系統傳送系統呼叫,然後再由操作將其
轉換成具體的硬碟操作。
1.3 如何用檔案:open( )
1.3.1 控制檔案讀寫內容的模式:t和b
強調: t和b不能單獨使用,必須跟r/w/a連用,預設就是t, t代表text,通常不寫
t文字(預設的模式)
- 讀寫都以str(unicode)為單位
- 文字檔案
- 必須指定encoding='utf-8'
b二進位制/bytes
- 一般是一些音訊檔案或者視訊檔案
1.3.2 控制檔案讀寫操作的模式
- r 只讀模式
- w 只寫模式
- a 只追加寫模式
- +: r+, w+, a+ 在原來模式的基礎上只讀的變可讀可寫,可寫的變可寫可讀
2. 檔案操作的基本流程
2.1 開啟檔案
open("檔案路徑") open("C:\a\b\c\d.txt") # 但是\(右斜槓)在字串中轉義的作用 # 如果資料夾叫nb,那寫成路徑\nb在字串中就是換行符的意思了
2.1.1 如何取消轉義?
- 字串前+r,eg:
r"C:\a\b\nb\d.txt"
- 用左斜槓代替右斜槓,open功能自動會把左斜槓替換成右斜槓
2.1.2 路徑分割符
- win下預設的路徑分隔符是
右斜槓\
- linux下預設的路徑分隔符是
左斜槓/
2.1.3 絕對路徑和相對路徑
絕對路徑就是檔案的全部路徑,從開始的碟符一直寫到該檔案。
open(r'E:\project\python\s29code\day11\1.開啟檔案.py')
相對路徑就是以當前檔案所在的資料夾為基礎,然後找檔案,所以也只能找到同文件夾下的檔案
2.2 操作檔案
# 1. 開啟檔案,由應用程式向作業系統發起系統呼叫open(...), # 作業系統開啟該檔案,對應一塊硬碟空間,並返回一個檔案物件賦值給一個變數f f=open('a.txt',moder='r',encoding='utf-8') #預設開啟模式就為r # 2. 呼叫檔案物件的讀/寫方法,會被作業系統轉換為讀/寫硬碟的操作 data=f.read() # 讀取檔案內容,將硬碟中的二進位制資料讀取到記憶體中 --> t控制將二進位制轉換成字元
2.3 關閉檔案
開啟一個檔案包含兩部分資源:應用程式的變數f和作業系統開啟的檔案。在操作完畢一個檔案時,必須把與該文
件的這兩部分資源全部回收,回收方法為:
1、f.close() #回收作業系統開啟的檔案資源,一旦關閉就不能操作了,但是f變數還在
2、del f #回收應用程式級的變數,一般我們不用寫。
其中del f一定要發生在f.close( ) 之後,否則就會導致作業系統開啟的檔案無法關閉,白白佔用資源, 而python
自動的垃圾回收機制決定了我們無需考慮del f,這就要求我們,在操作完畢檔案後,一定要記住f.close( )。
2.4 with上下文管理器
with上文幫你開啟檔案,下文就是幫你自動關閉檔案.
with open('a.txt',mode='rt') as f1:
res = f1.read() # 讀取檔案內容,硬碟的搬運工
print(res)
# f1稱之為檔案物件/檔案控制代碼,就是用來控制檔案的
# 當with語句的程式碼塊執行完畢後會自動執行f.close()
# 開啟多個檔案
with open('a.txt',mode='rt') as f1,open('b.txt',mode='rt') as f2:
#with open('a.txt',mode='rt') as f1,\
# open('b.txt',mode='rt') as f2: 通過右斜槓將一句程式碼換行寫
res1 = f1.read() # 讀取檔案內容
res2 = f2.read()
print(res1)
print(res2)
2.5 指定操作檔案的字元編碼
open功能還有一個引數就是encoding='解碼格式'
with open(r'a.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read() # t模式會將f.read()讀出來的結果解碼成unicode
print(res, type(res))
# 記憶體: utf-8格式的二進位制 ----> 解碼 ----> unicode
# 硬碟(a.txt內容: utf-8格式的二進位制)
如果你不指定encoding引數的話,作業系統會使用自己的預設編碼進行解碼。
- mac/linux utf-8
- windows gbk
3. 檔案的操作模式
3.1 控制檔案操作的三種模式
r(預設的):只讀
w:只寫
a:只追加寫
3.1.1 r 模式的使用
r (預設的操作模式):只讀模式,當檔案不存在時,會報錯。
with open(r'd.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read()
print(res, type(res))
# 執行結果:
Traceback (most recent call last):
File "E:/project/python/s29code/day11/2.r模式.py", line 1, in <module>
with open(r'd.txt', mode='rt',encoding='utf-8') as f1:
FileNotFoundError: [Errno 2] No such file or directory: 'd.txt'
當檔案存在時,檔案指標跳到開始位置,所以當我們f.read()
時會把檔案指標從頭讀到尾,即一次性
把檔案內容全部從硬碟載入到記憶體中。注意:大檔案不能這樣讀,如果內容過大,會撐爆記憶體
# a.txt
你好啊
with open(r'a.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read() # 把所有內容從硬碟讀入記憶體
print(res) # 你好啊
3.1.2 w模式的使用
w模式,是隻寫模式,當檔案不存在的時候,用w模式開啟的話,會自動在該路徑建立一個新的檔案。
當檔案存在時,w模式會先清空原來的內容,指標位於開始位置,然後再寫內容。
with open(r'e.txt', mode='w',encoding='utf-8') as f1:
f1.write("你好")
注意:w模式不是覆蓋,可以自己嘗試一下,假設原檔案的內容很多,你用w模式開啟,然後只寫一個字元
試試。
但是如果我們用w模式沒有關閉,連續寫入內容的話,不會清空之前的內容,並緊跟在舊的內容後面。
with open('b.txt',mode='w',encoding='utf-8') as f:
f.write('大笨蛋\n')
f.write("大傻子\n")
f.write("二桿子")
# b.txt
大笨蛋
大傻子
二桿子
3.1.3 a模式的使用
a模式是隻追加寫模式,當檔案不存在時,和w模式一樣,會建立新檔案。
當檔案存在的時候,a模式開啟檔案,不會清空原檔案的內容,指標會自動移到檔案末尾。
with open('c.txt',mode="a",encoding="utf-8") as f:
f.write('我是第一行\n')
# c.txt
我是第一行
當檔案沒有關閉的情況,用a模式持續寫入的話,是和w模式一樣的,新內容會緊跟舊內容的後面
with open('c.txt',mode="a",encoding="utf-8") as f:
f.write('我是第一行\n')
f.write('我是第二行\n')
f.write('我是第三行\n')
f.write('我是第四行\n')
# c.txt
我是第一行
我是第二行
我是第三行
我是第四行
a模式和w模式的異同點:
- 相同點:只能寫,不能讀,當檔案沒有關閉時,連續寫入,新內容緊跟在就內容之後
- 不同點:a模式開啟已存在的檔案,不會清空原檔案內容,指標自動移動到檔案末尾
w模式和a模式的應用場景:
-
w模式一般用於新檔案的儲存,比如copy檔案底層就是把一個檔案讀出來,然後用w寫入到新檔案
-
a模式則是適用於在原檔案的基礎上寫新內容,比如記錄日誌,使用者註冊記錄檔案
3.1.4 瞭解+模式
+模式,不能單獨使用,只能和r,w或a模式一起使用
- r+ 讀寫模式,基準是r模式,當檔案不存在時,用r+開啟會報錯,指標在開頭。
- w+ 寫讀模式,基準是w模式,檔案不存在會建立檔案,檔案存在同樣會清空原檔案
- a+ 追加讀模式,基準是a模式,檔案不存在會建立檔案,指標在末尾
3.1.5 提一嘴x模式
x模式,只寫模式,當檔案不存在的時候會建立新檔案,指標在檔案開頭,但是當檔案存在的時候會直接報錯
# d.txt 已經存在的檔案
with open('d.txt',moder="x",encoding="utf-8") as f:
f.write("你好")
Traceback (most recent call last):
File "E:/project/python/s29code/day11/5.x模式.py", line 1, in <module>
with open('d.txt',mode='x',encoding='utf-8') as f:
FileExistsError: [Errno 17] File exists: 'd.txt'
3.2 控制檔案內容的兩種模式
大前提: tb模式均不能單獨使用,必須與r/w/a之一結合使用
t(預設的):文字模式
- 讀寫檔案都是以字串( unicode )為單位的
- 只能針對文字檔案
- 必須指定encoding引數
b:二進位制模式:
- 讀寫檔案都是以bytes/二進位制為單位的
- 可以針對所有檔案
- 一定不能指定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 模式時,內部幫我們做了編碼與解碼
# 如果我們用t模式開啟視訊檔案的話
with open('a.mp4',mode='rt',encoding='utf-8') as f:
... # ... == pass 這樣並不會報錯,因為我們只是呼叫了作業系統的介面,告訴他用rt開啟檔案
# 以後用utf-8對讀入記憶體的檔案進行解碼,但是我們還沒有讀/寫檔案,只是開啟檔案,所以不報錯
3.2.2 b模式
同樣b模式也是不能單獨使用的,必須和r、w、a配合使用,t模式只針對字元檔案,而b模式是針對所有檔案,包
包括文字檔案,但是一定要注意不能指定encoding引數,否則報錯。
with open('a.jpg',mode='rb') as f:
res = f.read() # 硬碟的二進位制讀入記憶體->b模式下,不做任何轉換,直接讀入記憶體
print(res,type(res)) # bytes型別 -> 當成二進位制
# b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x0C
# <class 'bytes'>
# 理論上我們應該讀出來的是物理層間的010101這種,但是如果以這種顯示的話,不利於閱讀,以及操作資料
# 所以python對讀出來的內容做了轉換,是16進位制的顯示,平常我們就把當成二進位制來看就行了.
with open('b.txt',mode='rb') as f:
res = f.read() # 把躺在硬碟的二進位制直接讀出來,不做任何轉換
print(res,type(res))
print(res.decode('utf-8')) # 檔案是以utf-8編碼格式存的,可以進行字元解碼
4. 迴圈讀取檔案的兩種方式
4.1 for迴圈一行一行的讀
for迴圈不但可以迴圈那些基礎資料型別,也可以迴圈檔案控制代碼, 每次迴圈,以行為單位
但是for迴圈有個侷限
# t模式
with open('b.txt',mode='r',encoding='utf-8') as f:
for line in f:
print(line)
# 列印內容:
# 是可撥打發紅包測
# 我付款金額日本
# 費勁兒家人
# b模式
with open('b.txt',mode='rb') as f:
for line in f:
print(line)
# 列印內容:
b'\xe6\x98\xaf\xe5\x8f\xaf\xe6\x8b\xa8\xe6\x89\x93\xe5\x8f\x91\xe7\xba\xa2\xe5\x8c\x85\xe6\xb5\x8b\r\n' # \r\n --> 換行符
b'\xe6\x88\x91\xe4\xbb\x98\xe6\xac\xbe\xe9\x87\x91\xe9\xa2\x9d\xe6\x97\xa5\xe6\x9c\xac\r\n'
b'\xe8\xb4\xb9\xe5\x8a\xb2\xe5\x84\xbf\xe5\xae\xb6\xe4\xba\xba\r\n'
b'\xe4\xbb\x98\xe9\x87\x91\xe9\xa2\x9d\xe6\x97\xa5\xe5\x93\xa6\xe5\x9b\x9e\xe9\xa1\xbe'
4.2 while迴圈
之前就說過for迴圈能實現的,while迴圈也能實現,迴圈讀取檔案也是可以的
# t模式和b模式都適用
with open('b.txt',mode='rb') as f:
while 1:
content = f.read(1024) # 可以指定讀取檔案內容的大小,以位元組為單位
if not content:
break
print(content)
4.3 小練習
檔案拷貝小程式,要求:字元檔案和文字檔案都適用。5.
file_path = input('請輸入要拷貝的檔案的絕對路徑:')
new_file_path = input('請輸入新檔案的絕對路徑:')
with open(file_path, mode='rb') as f, \
open(new_file_path, mode='wb') as f1:
for line in f:
f1.write(line)
5. f 的常用方法
5.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模式
5.2 瞭解操作
f.readable() # 檔案是否可讀
f.writable() # 檔案是否可讀
f.closed # 檔案是否關閉
f.encoding # 如果檔案開啟模式為b,則沒有該屬性
f.flush() # 立刻將檔案內容從記憶體刷到硬碟,一般情況下當所有寫操作完畢後,才會寫入硬碟,但是我們 # 可以用flush強制寫到硬碟
f.name # 返回檔名稱
6. 檔案指標的移動
f.seek(n,模式) n指的是移動的位元組個數
f.tell( ) 返回檔案指標當前位置
指標移動的單位都是以位元組(bytes)為單位
只有一種情況特殊:
t模式下的read(n) n代表字元的個數強調: t模式下只能用0模式,b模式下0,1,2都可以用
6.1三種模式
模式:
0:參照物是開頭位置
1:參照物是當前位置
2:參照物是檔案末尾
6.1.1 0模式
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
f.seek(6, 0)
f.seek(3,0)
# f.seek(3, 1) # 報錯
res = f.read()
print(res)
6.1.2 1模式
with open(r'a.txt', mode='rb') as f:
f.seek(6, 1)
f.seek(3,1)
#f.seek(300, 1) # 向右可以無限移動
# print(f.read())
res = f.tell()
print(res)
6.1.3 2模式
with open(r'a.txt', mode='rb') as f:
f.seek(-6, 2)
f.seek(-3, 2)
# f.seek(-30, 2) # 超過了最大的移動距離
# f.seek(0, 0)
# f.seek(-1, 0) # 報錯,開頭無法再向左移動了
res = f.tell() # 返回檔案指標的時候都是從左向右數
print(res)
6.1.4 小作業
# 寫一個動態更新顯示最新日誌的一個小程式
import time
with open(r'a.txt', mode='rb') as f:
# 將指標跳到檔案末尾
f.seek(0, 2)
while 1:
res = f.readline()
if len(res) == 0:
time.sleep(0.5)
else:
print(res.decode(), end='')
# 追加日誌的程式
with open(r'a.txt',mode='a+',encoding='utf-8') as f:
f.write('20201120 張三消費1000元\n')
6.2 修改檔案的兩種方式
首先補充一個知識點,就是硬碟的資料是不能修改的,記憶體的資料是可以修改的。比如有一個文字檔案,裡面有
資料:張三 北京大學 大一學生,你想修改成 張三 男 20 北京大學 大一學生。平常我們用的文字編輯器是可以
直接修改的。但是你用python程式碼試一下看一下結果。
with open(r'a.txt',mode='r+t',encoding='utf-8') as f:
f.seek(10,0) # 指標移動到張三後面
f.write('男 20')
# 當你再次開啟檔案你會發現 男 20會把北京大學覆蓋了
原因是硬碟不支援修改,或者說成是追加。如果一塊資料發生移動,那麼在它後面的所有資料都會移動,那麼每次
修改檔案都會產生這種問題。所以硬碟只能寫不能中間插入。而我們平常用的文字編輯器只是把記憶體中的所有修改
後的資料再儲存到硬碟上。
6.2.1 第一種方式:
with open( 'c.txt' ,mode='rt',encoding='utf-8' ) as f:
res = f.read ( )
data = res.replace( 'alex' , 'dsb')
print(data)
with open( 'c.txt' ,mode='wt',encoding='utf-8' ) as f1:
f1.write(data)
6.2.2 第二種方式:
import os
with open(r'c.txt', mode='r', encoding='utf-8') as f, \
open(r'.c.txt.swap', mode='w', encoding='utf-8') as f1:
# .原檔名.swap 是linux中的臨時交換的檔案命名
for line in f:
data = line.replace('alex', 'dsb')
f1.write(data)
os.remove('c.txt') # 刪除原檔案
os.rename('.c.txt.swap', 'c.txt') # 對新檔案重新命名