Python使用os模块实现更高效地读写文件

目录
  • 使用 os.open 打开文件
  • 使用 os.read 读取文件
  • 使用 os.write 写入文件

使用 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"))
"""
觉
"""

以上就是文件的写入,当然它也可以和内置函数 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"))

执行一下,查看控制台:

以上就是 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)

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

以上就是使用 os 模块操作文件,它是直接使用操作系统提供的系统调用,所以效率上会比内置函数 open 要高一些。但是工作中还是不太建议使用 os 模块操作文件,使用内置函数 open 就好。

到此这篇关于Python使用os模块实现更高效地读写文件的文章就介绍到这了,更多相关Python os模块读写文件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何在python中用os模块实现批量移动文件

    本文介绍一下利用python批量把一个文件夹(及其子文件夹)下面的特定类型的文件移动到另一个文件夹下 通过python操作系统目录及其文件,需要用到os包,本次具体用到如下方法 os.walk(), 这个方法可以遍历一个文件夹及其子文件(子子文件夹等) os.rename(),这个方法用于命名文件或目录(因为是操作一个文件的绝对路径,所以其实相当于对文件进行剪切操作) 另外也可以借助 shutil库对文件进行移动或复制操作 下面是示例代码 def run_main(source_dir, tar

  • python文件目录操作之os模块

    一.os函数目录 1 os.access(path, mode) 检验权限模式 2 os.chdir(path) 改变当前工作目录 3 os.chflags(path, flags) 设置路径的标记为数字标记. 4 os.chmod(path, mode) 更改权限 5 os.chown(path, uid, gid) 更改文件所有者 6 os.chroot(path) 改变当前进程的根目录 7 os.close(fd) 关闭文件描述符 fd 8 os.closerange(fd_low, fd

  • Python文件处理、os模块、glob模块

    目录 一.文件基本的操作 1.open() 打开文件 2.read() 读文件 3.write()写文件: 4.with open()方法 二.文件的打开模式 1.文件r打开模式 1.读文本 2.读字节 3.for逐行读取,相当于readline. 4.读方法: 2.文件W打开模式 1.文本模式 2.字节模式 3.写方法: 3.文件a打开模式 4.+可读.可写 三.文件指针操作 1.seek(offset, from_what) :改变当前文件操作指针的位置 2.tell():告诉文件当前位置.

  • Python常用模块os.path之文件及路径操作方法

    以下是 os.path 模块的几种常用方法: 方法 说明 os.path.abspath(path) 返回绝对路径 os.path.basename(path) 返回文件名 os.path.commonprefix(list) 返回list(多个路径)中,所有path共有的最长的路径 os.path.dirname(path) 返回文件路径 os.path.exists(path) 如果路径 path 存在,返回 True:如果路径 path 不存在,返回 False. os.path.lexi

  • 基于python实现上传文件到OSS代码实例

    基础环境 # +++++ 阿里云OSS开发指南里都有详细的步骤,在这里整理了一下自己需要的东西 # 确定开发环境,centOS默认安装了python2.7 # python -V # 安装python开发包 # yum install -y python-devel # 安装OSS的sdk # yum install -y python-pip # pip2.7 install oss2 # 验证oss2是否安装正确 ''' >>> import oss2 >>> os

  • python+os根据文件名自动生成文本

    有时我们有很多文件(如图片),我们需要对每一个文件进行操作. 我们还需要一份文件的名字来进行遍历,这时我们首先需要建立一份文件名单,有时还会对文件名做一定的筛选,如我们只选择jpg格式的图片. 本文就是利用一个文件夹下的几张图片完成这些操作:把所有文件名写入到一个文件中. 这可以进一步用来遍历处理数据 #! usr/bin/env python # coding: utf-8 # 为了自动获取文件夹下的名字并将其写入文件,然后利用该文件来处理对应的文件 import os import nump

  • Python使用os模块实现更高效地读写文件

    目录 使用 os.open 打开文件 使用 os.read 读取文件 使用 os.write 写入文件 使用 os.open 打开文件 无论是读文件还是写文件,都要先打开文件.说到打开文件,估计首先想到的就是内置函数 open(即 io.open),那么它和 os.open 有什么关系呢? 内置函数 open 实际上是对 os.open 的封装,在 os.open 基础上增加了相关访问方法.因此为了操作方便,应该调用内置函数 open 进行文件操作,但如果对效率要求较高的话,则可以考虑使用 os

  • python中os模块详解

    os模块提供了对目录或者文件的新建/删除/查看文件属性,还提供了对文件以及目录的路径操作.比如说:绝对路径,父目录-- os.sep可以取代操作系统特定的路径分隔符.windows下为 "\\",Linux下为"/" os.linesep字符串给出当前平台使用的行终止符.例如,Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'. os.pathsep 输出用于分割文件路径的字符串,系统使用此字符来分割搜索路径(像PATH),例如POSIX上'

  • python下os模块强大的重命名方法renames详解

    python下os模块强大的重命名方法renames详解 在python中有很多强大的模块,其中我们经常要使用的就是OS模块,OS模块提供了超过200个方法来供我们使用,并且这些方法都是和数据处理相关的,这里介绍下重命名这个方法. OS的重命名方法是os.rename,我用的ipython,这个玩意很是强大,只要按下TAB键,可以帮助我们自动对齐和列出可以使用的方法,发现有2个方法,分别是rename和renames,2个方法,前面的rename使用过无数次,但是后面的renames还没有使用过

  • Python中os模块功能与用法详解

    本文实例讲述了Python中os模块功能与用法.分享给大家供大家参考,具体如下: OS模块 Python的os模块封装了常见的文件和目录操作,本文只是列出部分常用的方法,更多的方法可以查看官方文档. 下面是部分常见的用法: 方法 说明 os.mkdir 创建目录 os.rmdir 删除目录 os.rename 重命名 os.remove 删除文件 os.getcwd 获取当前工作路径 os.walk 遍历目录 os.path.join 连接目录与文件名 os.path.split 分割文件名与目

  • 浅谈Python中os模块及shutil模块的常规操作

    如下所示: #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表.这个列表以字母顺序. 它不包括 '.' 和'..' 即使它在文件夹中. #只支持在 Unix, Windows 下使用 import os, sys # 打开文件 path=r'C:\Users\Administrator.SKY-20180518VHY\Desktop\rx\ore' dirs = os.listdir( path ) print(dirs) # 输出所有文件和文件夹 for fil

  • python利用os模块编写文件复制功能——copy()函数用法

    我就废话不多说了,大家还是直接看代码吧~ #文件复制 import os src_path=r'E:\Pycharm\python100题\代码' target_path=r'E:\Pycharm\python100题\123' #封装成函数 def copy_function(src,target): if os.path.isdir(src) and os.path.isdir(target): filelist=os.listdir(src) for file in filelist: p

  • python 中os模块os.path.exists()的用法说明

    os即operating system(操作系统),Python 的 os 模块封装了常见的文件和目录操作. os.path模块主要用于文件的属性获取,exists是"存在"的意思,所以顾名思义,os.path.exists()就是判断括号里的文件是否存在的意思,括号内的可以是文件路径. 举个栗子: user.py为存在于当前目录的一个文件 输入代码: import os path = os.path.exists('user.py') print(path) 输出结果: True P

  • Python中os模块的简单使用及重命名操作

    前言 OS模块虽然基础的时候已经学过了,但是谁让本人属于那种不用立马就忘的人呢,所以在在下爬取某个不可名状的男人都喜欢的网站的时候,在遇到爬取下来的数据需要保存的时候,就需要用到OS模块了 OS模块基础回顾 先回顾一下基础 OS模块用于操作文件夹(基于我的理解) import os os.mkdir("path") # 创建该路径 然后,基础就没了,对的,我学基础OS模块的时候,就学了个这个,在当时来看,还是够用的啦 OS模块小应用 这下面就是本人在写爬虫小程序的时候用到的啦 1. 保

  • Python中os模块的实例用法

    1.说明 os.path.exists():用于判断某个路径(文件或文件夹)是否存在,若存在则返回True,若不存在则返回False. os.makedirs():用于创建文件夹.传入所欲创建的文件夹的路径即可,没有返回值.值得一提的是,这个函数可以实现目录的递归创建,也就是说如果所传入的路径中,倒数第二级的目录也不存在,那么就会先创建该级目录,然后在在目录下创建所欲创建的目录,依此类推. os.path.basename():传入一个文件的路径,返回该文件的文件名. os.path.dirna

  • Python之os模块案例详解

    OS模块 import os 1.返回操作系统类型 :posix 是linux操作系统,nt 是windows操作系统 print(os.name) print('Linux' if os.name == 'posix' else 'Windows') posix Linux 2.操作系统的详细信息 info = os.uname() print(info) print(info.sysname) print(info.nodename) posix.uname_result(sysname='

随机推荐