中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python怎么使用os模塊實現更高效地讀寫文件

發布時間:2022-07-20 16:56:33 來源:億速云 閱讀:132 作者:iii 欄目:開發技術

這篇文章主要介紹“Python怎么使用os模塊實現更高效地讀寫文件”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Python怎么使用os模塊實現更高效地讀寫文件”文章能幫助大家解決問題。

使用 os.open 打開文件

無論是讀文件還是寫文件,都要先打開文件。說到打開文件,估計首先想到的就是內置函數 open(即 io.open),那么它和 os.open 有什么關系呢?

內置函數 open 實際上是對 os.open 的封裝,在 os.open 基礎上增加了相關訪問方法。因此為了操作方便,應該調用內置函數 open 進行文件操作,但如果對效率要求較高的話,則可以考慮使用 os.open。

此外 open 函數返回的是一個文件對象,我們可以在此基礎上進行任意操作;而 os.open 返回的是一個文件描述符,說白了就是一個整數,因為每一個文件對象都會對應一個文件描述符。

import os

f1 = 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。

Python怎么使用os模塊實現更高效地讀寫文件

操作系統提供了很多的系統調用,打開文件則是 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_RDONLY
open里面的寫模式等價于這里的 os.O_WRONLY | os.O_CREATE | os.O_TRUNC
open里面的追加模式等價于這里的 os.O_WRONLY | os.O_CREATE | os.O_APPEND
"""

好,打開方式介紹完了,那么怎么讀取和寫入呢?很簡單,讀取使用 os.read,寫入使用 os.write。

使用 os.read 讀取文件

先來看讀取,os.read 接收兩個參數,第一個參數是文件描述符,第二個參數是要讀取多少個字節。

import os

fd = 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 os

fd = 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 個字節
# 沒錯,此時作用類似于 input
while True:
    data = os.read(0, 10).strip()
    print(f"你輸入了:", data)
    if data == b"exit":
        break

我們測試一下:

Python怎么使用os模塊實現更高效地讀寫文件

os.read 可以實現 input 的效果,并且效率更高。另外當按下回車時,換行符也會被讀進去,所以需要 strip 一下。然后我們這里讀的是 10 個字節,如果一次讀不完,那么會分多次讀取。在讀取文件的時候,也是同理。

from io import BytesIO
import os

fd = os.open("main.c", os.O_RDONLY)
buf = BytesIO()

while True:
    data = os.read(fd, 10)
    if data != b"":
        buf.write(data)
    else:
        break
print(buf.getvalue().decode("utf-8"))
"""
#include <Python.h>
#include <ctype.h>

"""

然后 os.read 還可以和內置函數 open 結合,舉個栗子:

import os
import io

f = open("main.c", "r")
# 通過 f.fileno() 即可拿到對應的文件描述符
# 雖然這里是以文本模式打開的文件
# 但只要拿到文件描述符,都可以交給 os.read
print(
    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())  # 41

try:
    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())  # 41
os.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 os

os.write(1, "往 stdout 里面寫入\n".encode("utf-8"))
os.write(2, "往 stderr 里面寫入\n".encode("utf-8"))

執行一下,查看控制臺:

Python怎么使用os模塊實現更高效地讀寫文件

以上就是 os.write 的用法。

最后是關閉文件,使用 os.close 即可。

import os
import io

fd = 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 os

f = open(r"1.txt", "r")
# 文件是否關閉
print(f.closed)  # False
os.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)

該方法不是很常用,了解一下即可。

關于“Python怎么使用os模塊實現更高效地讀寫文件”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

林州市| 光泽县| 邯郸县| 中方县| 江源县| 龙江县| 正镶白旗| 晋中市| 婺源县| 嵩明县| 平昌县| 永宁县| 荔浦县| 浙江省| 长白| 西吉县| 禹州市| 灌云县| 华池县| 蒙城县| 宁陕县| 襄城县| 营山县| 和硕县| 乌拉特前旗| 望城县| 临潭县| 洛隆县| 武定县| 达日县| 合川市| 威远县| 长顺县| 南丹县| 揭阳市| 即墨市| 海阳市| 铜山县| 翁牛特旗| 大兴区| 普宁市|