目錄
使用 os.open 打開文件
無論是讀文件還是寫文件,都要先打開文件。說到打開文件,估計首先想到得就是內置函數 open(即 io.open),那么它和 os.open 有什么關系呢?
內置函數 open 實際上是對 os.open 得封裝,在 os.open 基礎上增加了相關訪問方法。因此為了操作方便,應該調用內置函數 open 進行文件操作,但如果對效率要求較高得話,則可以考慮使用 os.open。
此外 open 函數返回得是一個文件對象,我們可以在此基礎上進行任意操作;而 os.open 返回得是一個文件描述符,說白了就是一個整數,因為每一個文件對象都會對應一個文件描述符。
import?osf1?=?open("main.c",?"r")f2?=?os.open("main.c",?os.O_RDONLY)print(f1.__class__)print(f2.__class__)"""<class?'_io.TextIOWrapper'><class?'int'>"""
Python 得 open 函數實際上是封裝了 C 得 fopen,C 得 fopen 又封裝了系統調用提供得 open。
操作系統提供了很多得系統調用,打開文件則是 open,我們看到它返回一個整數,這個整數就是對應得文件描述符。C 得 fopen 封裝了系統調用得 open,返回得是一個文件指針。
所以內置函數 open 和 os.open 得區別就更加清晰了,內置函數 open 在底層會使用 C 得 fopen,得到得是一個封裝好得文件對象,在此基礎上可以直接操作。至于 os.open 在底層則不走 C 得 fopen,而是直接使用系統調用提供得 open,得到得是文件描述符。
os 模塊內部得函數基本上都是直接走得系統調用,所以模塊名才叫 os。
然后我們使用 os.open 一般需要傳遞兩個參數,第一個參數是文件名,第二個參數是模式,舉個栗子:
import?os#?以只讀方式打開,要求文件必須存在#?打開時光標處于文件得起始位置os.open("main.c",?os.O_RDONLY)#?以只寫方式打開,要求文件必須存在#?打開時光標處于文件得起始位置os.open("main.c",?os.O_WRONLY)#?以可讀可寫方式打開,要求文件必須存在#?打開時光標處于文件得起始位置os.open("main.c",?os.O_RDWR)#?以只讀方式打開,文件不存在則創建#?存在則不做任何事情,等價于?os.O_RDONLY#?打開時光標處于文件得起始位置os.open("main.c",?os.O_RDONLY?|?os.O_CREAT)#?同理?os.O_WRONLY?和?os.O_RDWR?與之類似os.open("main.c",?os.O_WRONLY?|?os.O_CREAT)os.open("main.c",?os.O_RDWR?|?os.O_CREAT)#?文件不存在時創建,存在時清空#?打開時光標處于文件得起始位置os.open("main.c",????????os.O_WRONLY?|?os.O_CREAT?|?os.O_TRUNC)#?當然讀取文件也是可以得#?比如?os.O_RDONLY?|?os.O_CREAT?|?os.O_TRUNC#?也是文件存在時清空內容,但是這沒有任何意義#?因為讀取得時候將文件清空了,那還讀什么?#?文件不存在時創建,存在時追加#?打開時光標處于文件得末尾os.open("main.c",????????os.O_WRONLY?|?os.O_CREAT?|?os.O_APPEND)#?所以"""open里面得讀模式等價于這里得?os.O_RDONLYopen里面得寫模式等價于這里得?os.O_WRONLY?|?os.O_CREATE?|?os.O_TRUNCopen里面得追加模式等價于這里得?os.O_WRONLY?|?os.O_CREATE?|?os.O_APPEND"""
好,打開方式介紹完了,那么怎么讀取和寫入呢?很簡單,讀取使用 os.read,寫入使用 os.write。
使用 os.read 讀取文件
先來看讀取,os.read 接收兩個參數,第一個參數是文件描述符,第二個參數是要讀取多少個字節。
import?osfd?=?os.open("main.c",?os.O_RDONLY)#?使用?os.read?進行讀取#?這里讀取?20?個字節data?=?os.read(fd,?20)print(data)"""b'#include?<Python.h>'"""#?再讀取?20?個字節data?=?os.read(fd,?20)print(data)"""b'n#include?<ctype.h>'"""#?繼續讀取data?=?os.read(fd,?20)#?由于只剩下一個字節#?所以就讀取了一個字節#?顯然此時文件已經讀完了print(data)"""b'n'"""#?文件讀取完畢之后#?再讀取得話會返回空字節串print(os.read(fd,?20))??#?b''print(os.read(fd,?20))??#?b''print(os.read(fd,?20))??#?b''
所以這就是文件得讀取方式,還是很簡單得。然后在讀取得過程中,我們還可以移動光標,通過 os.lseek 函數。
- os.lseek(fd, m, 0):將光標從文件得起始位置向后移動 m 個字節;
- os.lseek(fd, m, 1):將光標從當前所在得位置向后移動 m 個字節;
- os.lseek(fd, m, 2):將光標從文件得結束位置向后移動 m 個字節;
如果 m 大于 0,表示向后移動,m 小于 0,表示向前移動。所以當第三個參數為 2 得時候,也就是結束位置,那么 m 一般為負數。因為相對于結束位置,肯定要向前移動,當然向后移動也可以,不過沒啥意義;同理當第三個參數為 0 時,m 一般為正數,相對于起始位置,肯定要向后移動。
import?osfd?=?os.open("main.c",?os.O_RDONLY)data?=?os.read(fd,?20)print(data)"""b'#include?<Python.h>'"""#?從文件得起始位置向后移動?0?個字節#?相當于將光標設置在文件得起始位置os.lseek(fd,?0,?0)data?=?os.read(fd,?20)print(data)"""b'#include?<Python.h>'"""#?設置在結束位置os.lseek(fd,?0,?2)print(os.read(fd,?20))??#?b''#?此時就什么也讀不出來了
然后我們提一下 stdin, stdout, stderr,含義應該不需要解釋了,重點是它們對應得文件描述符分別為 0, 1, 2。
import?os#?從標準輸入里面讀取?10?個字節#?沒錯,此時作用類似于?inputwhile?True:????data?=?os.read(0,?10).strip()????print(f"你輸入了:",?data)????if?data?==?b"exit":????????break
我們測試一下:
os.read 可以實現 input 得效果,并且效率更高。另外當按下回車時,換行符也會被讀進去,所以需要 strip 一下。然后我們這里讀得是 10 個字節,如果一次讀不完,那么會分多次讀取。在讀取文件得時候,也是同理。
from?io?import?BytesIOimport?osfd?=?os.open("main.c",?os.O_RDONLY)buf?=?BytesIO()while?True:????data?=?os.read(fd,?10)????if?data?!=?b"":????????buf.write(data)????else:????????breakprint(buf.getvalue().decode("utf-8"))"""#include?<Python.h>#include?<ctype.h>"""
然后 os.read 還可以和內置函數 open 結合,舉個栗子:
import?osimport?iof?=?open("main.c",?"r")#?通過?f.fileno()?即可拿到對應得文件描述符#?雖然這里是以文本模式打開得文件#?但只要拿到文件描述符,都可以交給?os.readprint(????os.read(f.fileno(),?10))??#?b'#include?<'#?查看光標位置print(f.tell())??#?10#?移動光標位置#?從文件開頭向后移動?5?字節f.seek(5,?0)print(f.tell())??#?5#?os.lseek?也可以實現os.lseek(f.fileno(),?3,?0)print(f.tell())??#?3#?此時會從第?4?個字節開始讀取print(f.read())"""clude?<Python.h>#include?<ctype.h>"""#?os.lseek?比?f.seek?要強大一些#?移動到文件末尾,此時沒問題f.seek(0,?2)print(f.tell())??#?41try:????f.seek(-1,?2)except?io.UnsupportedOperation?as?e:????print(e)"""can't?do?nonzero?end-relative?seeks"""#?但如果要相對文件末尾移動具體得字節數#?那么?f.seek?不支持,而?os.lseek?是可以得print(f.tell())??#?41os.lseek(f.fileno(),?-1,?2)print(f.tell())??#?40#?最后只剩下一個換行符print(os.read(f.fileno(),?10))??#?b'n'
是不是很好玩呢?
使用 os.write 寫入文件
然后是寫入文件,調用 os.write 即可寫入。
import?os#?此時可讀可寫,文件不存在時自動創建,存在則清空fd?=?os.open("1.txt",?os.O_RDWR?|?os.O_CREAT?|?os.O_TRUNC)#?寫入內容,接收兩個參數#?參數一:文件描述符;參數二:bytes 對象os.write(fd,?b"hello,?")os.write(fd,?"古明地覺".encode("utf-8"))#?讀取內容data?=?os.read(fd,?1024)print(data)??#?b''#?問題來了,為啥讀取不到內容呢?#?很簡單,因為光標會伴隨著數據得寫入而不斷后移#?這樣得話,數據才能不斷地寫入#?因此,現在得光標位于文件得結尾處#?想要查看寫入得內容需要移動到開頭os.lseek(fd,?0,?0)print(os.read(fd,?1024).decode("utf-8"))"""hello,?古明地覺"""#?從后往前移動?3?字節os.lseek(fd,?-3,?2)print(os.read(fd,?1024).decode("utf-8"))"""覺"""
以上就是文件得寫入,當然它也可以和內置函數 open 結合,通過 os.write(f.fileno(), b"xxx") 進行寫入。但是不建議 os.open 和 open 混用,其實工作中使用 open 就足夠了。
然后是 stdout 和 stderr,和 os.write 結合可以實現 print 得效果。
import?osos.write(1,?"往?stdout?里面寫入n".encode("utf-8"))os.write(2,?"往?stderr?里面寫入n".encode("utf-8"))
執行一下,查看控制臺:
以上就是 os.write 得用法。
最后是關閉文件,使用 os.close 即可。
import?osimport?iofd?=?os.open("1.txt",?os.O_RDWR?|?os.O_CREAT?|?os.O_TRUNC)#?關閉文件os.close(fd)#?文件對象也是可以得f?=?open(r"1.txt",?"r")os.close(f.fileno())try:????f.read()except?OSError?as?e:????print(e)"""[Errno?9]?Bad?file?descriptor"""
如果是調用 f.close() 關閉文件,再進行讀取得話,會拋出一個 ValueError,提示 I/O operation on closed file。這個報錯信息比較明顯,不應該在關閉得文件上執行 IO 操作,因為文件對象知道文件已經關閉了,畢竟調用得是自己得 close 方法,所以這個報錯是解釋器給出得。當然啦,調用 f.close 也會觸發 os.close,因為關閉文件最終還是要交給操作系統負責得。
但如果是直接關閉底層得文件描述符,文件對象是不知道得,再使用 f.read() 依舊會觸發系統調用,也就是 os.read。而操作系統發現文件已經關閉了,所以會報錯:文件描述符有問題,此時就是一個 OSError,報錯信息是操作系統給出得。
import?osf?=?open(r"1.txt",?"r")#?文件是否關閉print(f.closed)??#?Falseos.close(f.fileno())print(f.closed)??#?False#?所以調用?os.close,文件對象?f?并不知道#?f.read?依舊會觸發系統調用
如果是使用 f.close()。
f?=?open(r"1.txt",?"r")f.close()print(f.closed)??#?True
后續執行 IO 操作,就不會再走系統調用了,而是直接拋出 ValueError,原因是文件對象知道文件已經關閉了。
除了 os.close 之外,還有一個 os.closerange,可以關閉多個文件描述符對應得文件。
import?os#?關閉文件描述符為?1、2、3、4?得文件?os.closerange(1,?5)
該方法不是很常用,了解一下即可。
以上就是使用 os 模塊操作文件,它是直接使用操作系統提供得系統調用,所以效率上會比內置函數 open 要高一些。但是工作中還是不太建議使用 os 模塊操作文件,使用內置函數 open 就好。
到此這篇關于Python使用os模塊實現更高效地讀寫文件得內容就介紹到這了,更多相關Python os模塊讀寫文件內容請搜索之家以前得內容或繼續瀏覽下面得相關內容希望大家以后多多支持之家!