Python使用os模块实现更高效地读写文件
作者:古明地觉 发布时间:2021-08-11 00:41:39
使用 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。
操作系统提供了很多的系统调用,打开文件则是 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
我们测试一下:
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"))
"""
觉
"""
来源:https://mp.weixin.qq.com/s/8ezt1rGx3Rhg_Kt-P6q4hA


猜你喜欢
- 1. 递归1.1 定义函数作为一种代码封装, 可以被其他程序调用,当然,也可以被函数内部代码调用。这种函数定义中调用函数自身的方式称为递归。
- 工作过程中遇到一个Js从Cookies里面取值的需求,Js貌似没有现成的方法可以指定Key值获取Cookie里面对应的值,参阅网上的代码,简
- tensorflow利用anaconda在ubuntu下安装方法及jupyter notebook运行目录及远程访问配置Ubuntu下安装A
- 一、rsa库(推荐)1、公钥加密、私钥解密# -*- coding: utf-8 -*-import rsa# rsa加密def rsaEn
- 引言图片减去均值后,再进行训练和测试,会提高速度和精度。因此,一般在各种模型中都会有这个操作。那么这个均值怎么来的呢,实际上就是计算所有训练
- 下面这个截图,就是使用 schedule 定时执行 Notebook 的例子import scheduleimport timeimport
- 目录前言1 异常类型1.1 Python内置异常1.2 requests模块的相关异常1.3 用户自定义异常2. 异常捕获2.1&
- 8是典型的七段数码管的例子,因为刚好七段都有经过,这里我写的代码是从1开始右转。这是看Mooc视频写的一个关于用七段数码管显示当前时间# -
- 项目地址https://github.com/jonssonyan...开发工具 python 3.7.9pycharm 2019.3.5
- 本文实例讲述了Python3基于sax解析xml操作。分享给大家供大家参考,具体如下:python使用SAX解析xmlSAX是一种基于事件驱
- django中,很多时候我们都需要有一个地方来进行更加详细的权限控制,例如说哪些用户可以访问哪些页面,检查登录状态等,这里的话就涉及到了中间
- 前言项目开发中,产品经理提了这样一个需求:将系统中的附件实现批量打包下载功能。本来系统中是有单个下载及批量下载功能,现在应业务方的需求,需要
- IDA Pro 6.0使用Qt 框架实现了跨平台的UI。它的好处是插件编写者还可以直接使用 Qt 开发跨平台 UI。但是编剧呢?在这篇博文中
- 在用户注册中最常见的安全验证之一就是邮箱验证。根据行业的一般做法,进行邮箱验证是避免潜在的安全隐患一种非常重要的做法,现在就让我们来讨论一下
- 前言最近使用pytorch训练模型,保存模型后再次加载使用出现了一些问题。记录一下解决方案!一、torch中模型保存和加载的方式1、模型参数
- 目录楔子faker使用方法基本使用地理信息类基础信息类计算机基础、Internet信息类网络基础信息类浏览器信息类数字类文本、加密类时间信息
- 1. 引言在Python中有很多好玩的花式打印,对厉害的高手来说可能是小菜一碟,对入门的小白来说往往让人望而退步,我们今天就来挑战下面三个常
- Request 对象在 scrapy 中 Request 对象代表着请求,即向服务器发送数据,该对象的构造函数原型如下所示:def __in
- 本文实例讲述了JS 设计模式之:单例模式定义与实现方法。分享给大家供大家参考,具体如下:良好的设计模式可以显著提高代码的可读性,降低复杂度和
- 本文实例讲述了Python实现的拟合二元一次函数功能。分享给大家供大家参考,具体如下:背景:使用scipy拟合一元二次函数。参考:HYRY