一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

使用 os 模塊更高效地讀寫(xiě)文件

 古明地覺(jué)O_o 2022-12-08 發(fā)布于北京

使用 os.open 打開(kāi)文件

無(wú)論是讀文件還是寫(xiě)文件,都要先打開(kāi)文件。說(shuō)到打開(kāi)文件,估計(jì)首先想到的就是內(nèi)置函數(shù) open(即 io.open),那么它和 os.open 有什么關(guān)系呢?

內(nèi)置函數(shù) open 實(shí)際上是對(duì) os.open 的封裝,在 os.open 基礎(chǔ)上增加了相關(guān)訪問(wèn)方法。因此為了操作方便,應(yīng)該調(diào)用內(nèi)置函數(shù) open 進(jìn)行文件操作,但如果對(duì)效率要求較高的話,則可以考慮使用 os.open。

此外 open 函數(shù)返回的是一個(gè)文件對(duì)象,我們可以在此基礎(chǔ)上進(jìn)行任意操作;而 os.open 返回的是一個(gè)文件描述符,說(shuō)白了就是一個(gè)整數(shù),因?yàn)?span style="color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">每一個(gè)文件對(duì)象都會(huì)對(duì)應(yīng)一個(gè)文件描述符。

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 函數(shù)實(shí)際上是封裝了 C 的 fopen,C 的 fopen 又封裝了系統(tǒng)調(diào)用提供的 open。

系統(tǒng)提供了很多的系統(tǒng)調(diào)用,打開(kāi)文件則是 open,我們看到它返回一個(gè)整數(shù),這個(gè)整數(shù)就是對(duì)應(yīng)的文件描述符。C 的 fopen 封裝了系統(tǒng)調(diào)用的 open,返回的是一個(gè)文件指針。

所以?xún)?nèi)置函數(shù) open 和 os.open 的區(qū)別就更加清晰了,內(nèi)置函數(shù) open 在底層會(huì)使用 C 的 fopen,得到的是一個(gè)封裝好的文件對(duì)象,在此基礎(chǔ)上可以直接操作。至于 os.open 在底層則不走 C 的 fopen,而是直接使用系統(tǒng)調(diào)用提供的 open,得到的是文件描述符。

?os 模塊內(nèi)部的函數(shù)基本上都是直接走的系統(tǒng)調(diào)用,所以模塊名才叫 os。

然后我們使用 os.open 一般需要傳遞兩個(gè)參數(shù),第一個(gè)參數(shù)是文件名,第二個(gè)參數(shù)是模式,舉個(gè)栗子:

import os

# 以只讀方式打開(kāi),要求文件必須存在
# 打開(kāi)時(shí)光標(biāo)處于文件的起始位置
os.open("main.c", os.O_RDONLY)

# 以只寫(xiě)方式打開(kāi),要求文件必須存在
# 打開(kāi)時(shí)光標(biāo)處于文件的起始位置
os.open("main.c", os.O_WRONLY)

# 以可讀可寫(xiě)方式打開(kāi),要求文件必須存在
# 打開(kāi)時(shí)光標(biāo)處于文件的起始位置
os.open("main.c", os.O_RDWR)

# 以只讀方式打開(kāi),文件不存在則創(chuàng)建
# 存在則不做任何事情,等價(jià)于 os.O_RDONLY
# 打開(kāi)時(shí)光標(biāo)處于文件的起始位置
os.open("main.c", os.O_RDONLY | os.O_CREAT)

# 同理 os.O_WRONLY 和 os.O_RDWR 與之類(lèi)似
os.open("main.c", os.O_WRONLY | os.O_CREAT)
os.open("main.c", os.O_RDWR | os.O_CREAT)

# 文件不存在時(shí)創(chuàng)建,存在時(shí)清空
# 打開(kāi)時(shí)光標(biāo)處于文件的起始位置
os.open("main.c",
        os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
# 當(dāng)然讀取文件也是可以的
# 比如 os.O_RDONLY | os.O_CREAT | os.O_TRUNC
# 也是文件存在時(shí)清空內(nèi)容,但是這沒(méi)有任何意義
# 因?yàn)樽x取的時(shí)候?qū)⑽募蹇樟耍沁€讀什么?

# 文件不存在時(shí)創(chuàng)建,存在時(shí)追加
# 打開(kāi)時(shí)光標(biāo)處于文件的末尾
os.open("main.c",
        os.O_WRONLY | os.O_CREAT | os.O_APPEND)

# 所以
"""
open里面的讀模式等價(jià)于這里的 os.O_RDONLY
open里面的寫(xiě)模式等價(jià)于這里的 os.O_WRONLY | os.O_CREATE | os.O_TRUNC
open里面的追加模式等價(jià)于這里的 os.O_WRONLY | os.O_CREATE | os.O_APPEND
"""

好,打開(kāi)方式介紹完了,那么怎么讀取和寫(xiě)入呢?很簡(jiǎn)單,讀取使用 os.read,寫(xiě)入使用 os.write。

使用 os.read 讀取文件

來(lái)看讀取,os.read 接收兩個(gè)參數(shù),第一個(gè)參數(shù)是文件描述符,第二個(gè)參數(shù)是要讀取多少個(gè)字節(jié)。

import os

fd = os.open("main.c", os.O_RDONLY)
# 使用 os.read 進(jìn)行讀取
# 這里讀取 20 個(gè)字節(jié)
data = os.read(fd, 20)
print(data)
"""
b'#include <Python.h>'
"""


# 再讀取 20 個(gè)字節(jié)
data = os.read(fd, 20)
print(data)
"""
b'\n#include <ctype.h>'
"""


# 繼續(xù)讀取
data = os.read(fd, 20)
# 由于只剩下一個(gè)字節(jié)
# 所以就讀取了一個(gè)字節(jié)
# 顯然此時(shí)文件已經(jīng)讀完了
print(data)
"""
b'\n'
"""


# 文件讀取完畢之后
# 再讀取的話會(huì)返回空字節(jié)串
print(os.read(fd, 20))  # b''
print(os.read(fd, 20))  # b''
print(os.read(fd, 20))  # b''

所以這就是文件的讀取方式,還是很簡(jiǎn)單的。然后在讀取的過(guò)程中,我們還可以移動(dòng)光標(biāo),通過(guò) os.lseek 函數(shù)。

  • os.lseek(fd, m, 0):將光標(biāo)從文件的起始位置向后移動(dòng) m 個(gè)字節(jié);

  • os.lseek(fd, m, 1):將光標(biāo)從當(dāng)前所在的位置向后移動(dòng) m 個(gè)字節(jié);

  • os.lseek(fd, m, 2):將光標(biāo)從文件的結(jié)束位置向后移動(dòng) m 個(gè)字節(jié);

如果 m 大于 0,表示向后移動(dòng),m 小于 0,表示向前移動(dòng)。所以當(dāng)?shù)谌齻€(gè)參數(shù)為 2 的時(shí)候,也就是結(jié)束位置,那么 m 一般為負(fù)數(shù)。因?yàn)橄鄬?duì)于結(jié)束位置,肯定要向前移動(dòng),當(dāng)然向后移動(dòng)也可以,不過(guò)沒(méi)啥意義;同理當(dāng)?shù)谌齻€(gè)參數(shù)為 0 時(shí),m 一般為正數(shù),相對(duì)于起始位置,肯定要向后移動(dòng)。

import os

fd = os.open("main.c", os.O_RDONLY)
data = os.read(fd, 20)
print(data)
"""
b'#include <Python.h>'
"""


# 從文件的起始位置向后移動(dòng) 0 個(gè)字節(jié)
# 相當(dāng)于將光標(biāo)設(shè)置在文件的起始位置
os.lseek(fd, 00)
data = os.read(fd, 20)
print(data)
"""
b'#include <Python.h>'
"""


# 設(shè)置在結(jié)束位置
os.lseek(fd, 02)
print(os.read(fd, 20))  # b''

# 此時(shí)就什么也讀不出來(lái)了

然后我們提一下 stdin, stdout, stderr,含義應(yīng)該不需要解釋了,重點(diǎn)是它們對(duì)應(yīng)的文件描述符分別為 0, 1, 2。

import os

# 從標(biāo)準(zhǔn)輸入里面讀取 10 個(gè)字節(jié)
# 沒(méi)錯(cuò),此時(shí)作用類(lèi)似于 input
while True:
    data = os.read(010).strip()
    print(f"你輸入了:", data)
    if data == b"exit":
        break

我們測(cè)試一下:

os.read 可以實(shí)現(xiàn) input 的效果,并且效率更高。另外當(dāng)按下回車(chē)時(shí),換行符也會(huì)被讀進(jìn)去,所以需要 strip 一下。然后我們這里讀的是 10 個(gè)字節(jié),如果一次讀不完,那么會(huì)分多次讀取在讀取文件的時(shí)候,也是同理。

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 還可以和內(nèi)置函數(shù) open 結(jié)合,舉個(gè)栗子:

import os
import io

f = open("main.c""r")
# 通過(guò) f.fileno() 即可拿到對(duì)應(yīng)的文件描述符
# 雖然這里是以文本模式打開(kāi)的文件
# 但只要拿到文件描述符,都可以交給 os.read
print(
    os.read(f.fileno(), 10)
)  # b'#include <'

# 查看光標(biāo)位置
print(f.tell())  # 10

# 移動(dòng)光標(biāo)位置
# 從文件開(kāi)頭向后移動(dòng) 5 字節(jié)
f.seek(50)
print(f.tell())  # 5
# os.lseek 也可以實(shí)現(xiàn)
os.lseek(f.fileno(), 30)
print(f.tell())  # 3
# 此時(shí)會(huì)從第 4 個(gè)字節(jié)開(kāi)始讀取
print(f.read())
"""
clude <Python.h>
#include <ctype.h>

"""


# os.lseek 比 f.seek 要強(qiáng)大一些
# 移動(dòng)到文件末尾,此時(shí)沒(méi)問(wèn)題
f.seek(02)
print(f.tell())  # 41

try:
    f.seek(-12)
except io.UnsupportedOperation as e:
    print(e)
"""
can't do nonzero end-relative seeks
"""

# 但如果要相對(duì)文件末尾移動(dòng)具體的字節(jié)數(shù)
# 那么 f.seek 不支持,而 os.lseek 是可以的
print(f.tell())  # 41
os.lseek(f.fileno(), -12)
print(f.tell())  # 40
# 最后只剩下一個(gè)換行符
print(os.read(f.fileno(), 10))  # b'\n'

是不是很好玩呢?

使用 os.write 寫(xiě)入文件

然后是寫(xiě)入文件,調(diào)用 os.write 即可寫(xiě)入。

import os

# 此時(shí)可讀可寫(xiě),文件不存在時(shí)自動(dòng)創(chuàng)建,存在則清空
fd = os.open("1.txt", os.O_RDWR | os.O_CREAT | os.O_TRUNC)
# 寫(xiě)入內(nèi)容,接收兩個(gè)參數(shù)
# 參數(shù)一:文件描述符;參數(shù)二:bytes 對(duì)象
os.write(fd, b"hello, ")
os.write(fd, "古明地覺(jué)".encode("utf-8"))
# 讀取內(nèi)容
data = os.read(fd, 1024)
print(data)  # b''
# 問(wèn)題來(lái)了,為啥讀取不到內(nèi)容呢?
# 很簡(jiǎn)單,因?yàn)楣鈽?biāo)會(huì)伴隨著數(shù)據(jù)的寫(xiě)入而不斷后移
# 這樣的話,數(shù)據(jù)才能不斷地寫(xiě)入
# 因此,現(xiàn)在的光標(biāo)位于文件的結(jié)尾處
# 想要查看寫(xiě)入的內(nèi)容需要移動(dòng)到開(kāi)頭
os.lseek(fd, 00)
print(os.read(fd, 1024).decode("utf-8"))
"""
hello, 古明地覺(jué)
"""

# 從后往前移動(dòng) 3 字節(jié)
os.lseek(fd, -32)
print(os.read(fd, 1024).decode("utf-8"))
"""
覺(jué)
"""

以上就是文件的寫(xiě)入,當(dāng)然它也可以和內(nèi)置函數(shù) open 結(jié)合,通過(guò) os.write(f.fileno(), b"xxx") 進(jìn)行寫(xiě)入。但是不建議 os.open 和 open 混用,其實(shí)工作中使用 open 就足夠了。

然后是 stdout 和 stderr,和 os.write 結(jié)合可以實(shí)現(xiàn) print 的效果。

import os

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

執(zhí)行一下,查看控制臺(tái):

以上就是 os.write 的用法。

最后是關(guān)閉文件,使用 os.close 即可。

import os
import io

fd = os.open("1.txt", os.O_RDWR | os.O_CREAT | os.O_TRUNC)
# 關(guān)閉文件
os.close(fd)

# 文件對(duì)象也是可以的
f = open(r"1.txt""r")
os.close(f.fileno())
try:
    f.read()
except OSError as e:
    print(e)
"""
[Errno 9] Bad file descriptor
"""

如果是調(diào)用 f.close() 關(guān)閉文件,再進(jìn)行讀取的話,會(huì)拋出一個(gè) ValueError,提示 I/O operation on closed file。這個(gè)報(bào)錯(cuò)信息比較明顯,不應(yīng)該在關(guān)閉的文件上執(zhí)行 IO 操作,因?yàn)槲募?duì)象知道文件已經(jīng)關(guān)閉了,畢竟調(diào)用的是自己的 close 方法,所以這個(gè)報(bào)錯(cuò)是解釋器給出的。當(dāng)然啦,調(diào)用 f.close 也會(huì)觸發(fā) os.close,因?yàn)殛P(guān)閉文件最終還是要交給操作系統(tǒng)負(fù)責(zé)的。

但如果是直接關(guān)閉底層的文件描述符,文件對(duì)象是不知道的,再使用 f.read() 依舊會(huì)觸發(fā)系統(tǒng)調(diào)用,也就是 os.read。而操作系統(tǒng)發(fā)現(xiàn)文件已經(jīng)關(guān)閉了,所以會(huì)報(bào)錯(cuò):文件描述符有問(wèn)題,此時(shí)就是一個(gè) OSError,報(bào)錯(cuò)信息是操作系統(tǒng)給出的。

import os

f = open(r"1.txt""r")
# 文件是否關(guān)閉
print(f.closed)  # False
os.close(f.fileno())
print(f.closed)  # False

# 所以調(diào)用 os.close,文件對(duì)象 f 并不知道
# f.read 依舊會(huì)觸發(fā)系統(tǒng)調(diào)用

如果是使用 f.close()。

f = open(r"1.txt""r")
f.close()
print(f.closed)  # True

后續(xù)執(zhí)行 IO 操作,就不會(huì)再走系統(tǒng)調(diào)用了,而是直接拋出 ValueError,原因是文件對(duì)象知道文件已經(jīng)關(guān)閉了。

除了 os.close 之外,還有一個(gè) os.closerange,可以關(guān)閉多個(gè)文件描述符對(duì)應(yīng)的文件。

import os

# 關(guān)閉文件描述符為 1、2、3、4 的文件 
os.closerange(15)

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

以上就是使用 os 模塊操作文件,它是直接使用操作系統(tǒng)提供的系統(tǒng)調(diào)用,所以效率上會(huì)比內(nèi)置函數(shù) open 要高一些。但是工作中還是不太建議使用 os 模塊操作文件,使用內(nèi)置函數(shù) open 就好。

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    国产精品超碰在线观看| 亚洲精品日韩欧美精品| 一区二区三区亚洲国产| 亚洲精品偷拍一区二区三区| 99少妇偷拍视频在线| 免费精品国产日韩热久久| 国产日韩欧美国产欧美日韩 | 国产精品视频久久一区| 蜜桃臀欧美日韩国产精品| 日韩女优精品一区二区三区| 91精品蜜臀一区二区三区| 91一区国产中文字幕| 精品推荐久久久国产av| 日本成人三级在线播放| 国产亚州欧美一区二区| 欧美成人精品国产成人综合| 欧美午夜一级特黄大片| 国产精品九九九一区二区| 午夜精品一区二区三区国产| 欧美综合色婷婷欧美激情| 丰满少妇被猛烈撞击在线视频| 免费一级欧美大片免费看| 亚洲最新中文字幕一区| 精品日韩av一区二区三区| 一区二区不卡免费观看免费| 中文字幕亚洲人妻在线视频 | 在线观看国产成人av天堂野外| 欧美日韩国产福利在线观看| 欧美大胆美女a级视频| 国产又粗又猛又大爽又黄同志| 欧美欧美欧美欧美一区| 亚洲一区二区三区在线免费| 老熟女露脸一二三四区| 五月激情五月天综合网| 情一色一区二区三区四| 亚洲欧洲日韩综合二区| 欧美区一区二在线播放| 欧美一区二区三区99| 午夜国产精品国自产拍av| 国产性情片一区二区三区| 国产午夜福利片在线观看|