python 文件操作(读写等)
阅读原文时间:2023年07月10日阅读:1

在实际开发中我们需要对文件做一些操作,例如读写文件、在文件中新添内容等,通常情况下,我们会使用open函数进行相关文件的操作,下面将介绍一下关于open读写的相关内容。

open方法打开文件并返回一个文件对象,之后对文件的处理均需要基于返回的文件对象,如果文件不存在或者无法打开,则抛出OSError,详细方法如下:

open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

常用参数

file: 需要打开的文件或者文件描述符,通常直接时文件即可

mode:对于文件需要操作的模式,常用的如下:

字符

含意

'r'

读取(默认)

'w'

写入,并先截断文件

'x'

排它性创建,如果文件已存在则失败

'a'

打开文件用于写入,如果文件存在则在末尾追加

'b'

二进制模式

't'

文本模式(默认)

'+'

打开用于更新(读取与写入)

encoding:操作文件时的编码,如果所读文件中包含中文,最好设置为utf-8格式,否则中文读出来会是乱码;如果是读取文件的原生字节的格式,则不能设置encoding。

不常用参数

buffering:是否采用行缓冲,0:不采用(适用于二进制模式),默认为1

默认的缓冲策略

* 二进制文件以固定大小的块进行缓冲;使用启发式方法选择缓冲区的大小,尝试确定底层设备的“块大小”或使用 io.DEFAULT_BUFFER_SIZE。在许多系统上,缓冲区的长度通常为4096或8192字节。
* “交互式”文本文件( isatty() 返回 True 的文件)使用行缓冲。其他文本文件使用上述策略用于二进制文件。

errors:可选字符串参数,用于指定如何处理编码和解码错误 - 这不能在二进制模式下使用。

newline:控制 universal newlines 模式如何生效(它仅适用于文本模式)。它可以是 None,'','\n','\r' 和 '\r\n,主要在读取流或者写入流时做处理,具体如下:

读取流时:如果newlines为None,则启用通用的换行模式,也可以使用其他可选参数
写入流时: 如果newlines为None,则会默认将\n转换为系统分隔符(os.linesep),如果不为None,则使用给定字符串

closefd:关闭文件描述符(只在file为文件描述符的情况下生效,否则会报错),默认为True,关闭文件描述符,当为False时,文件描述符在操作时会出现,文件关闭后,对应的文件描述符不会关闭。

opener:自定义开启器,注意必须要返回一个文件描述符,正常默认为None即可,详单与os.open() as opener

open方法返回了一个文件对象,其实就是IO对象,我们需要知道IO对象中部分方法或者函数。

读取内容

  • read(size): 读取内容,按照size的大小读取内容,可能会出现裁切的情况,当文件足够大时,size的大小决定了程序占用内存的大小,建议不要太大。
  • readable():是否可以读取流,可以则返回True,反之为False,当为False时则无法读取文件流内容。
  • readline(size=- 1):一行一行读取,可以设置size大小,代表最多读取的大小,主要是对于一些异常情况,例如一行有比较长的字符的时候
  • readlines(hint=- 1):读取整个文件的内容并安装换行符进行输出,返回一个列表,hint可以指定读取的行数。官方建议,如果可以使用 for line in file,就不需要使用for line in file.readlines()

写入内容

  • writable():是否可以写入流,可以则返回True,反之为False,当为False时无法写入文件。
  • writelines(lines): 写入多行数据,传入的是序列,默认会在每一个序列中的元素后添加换行
  • write(line: str):写入单行数据,传入的字符串

其他

  • isatty(): 如果流是交互式的(即连接到终端/tty设备),则返回 True

  • fileno(): 返回流的底层文件描述符(整数)---如果存在。如果 IO 对象不使用文件描述符,则会引发 OSError 。

  • tell():返回当前流的位置。与seek()中位置类似。需要注意的是,想要获取正确的位置,不能使用如下方式进行操作,会出现下方问题。

    with open(file_path, mode="r") as f:
        for line in f:
            print(line)
            print(f.tell())
    
    OSError: telling position disabled by next() call

    需要使用readline逐行读取

    with open(file_path, mode="r") as f:
        while True:
            line = f.readline()
            print(line)
            if not line:
                break
            print(f.tell())
  • seekable():如果流支持随机访问则返回 True。 如为 False,则 seek(), tell() 和 truncate() 将引发 OSError。

  • seek(offset, whence=SEEK_SET):将流位置修改到给定的字节 offset。 offset 将相对于由 whence 指定的位置进行解析。 whence 的默认值为 SEEK_SET。 需要注意的是seek的偏移量是相对于字符的,不是相对于行数的,whence 的可用值有:

    SEEK_SET 或 0 -- 流的开头(默认值);offset 应为零或正值
    SEEK_CUR or 1 -- 当前流位置;offset 可以为负值
    SEEK_END or 2 -- 流的末尾;offset 通常为负值
  • fileno():返回流的底层文件描述符(整数)---如果存在。如果 IO 对象不使用文件描述符,则会引发 OSError 。

  • flush():刷新流的写入缓冲区(如果适用)。因为某些情况下,在写入时会首先写入到缓冲区,为了保证数据全部写入,可以手动调用flush,这对只读和非阻塞流不起作用。

    返回新的绝对位置。

  • truncate(size=None):将流的大小调整为给定的 size 个字节(如果未指定 size 则调整至当前位置)。 当前的流位置不变。 这个调整操作可扩展或减小当前文件大小。 在扩展的情况下,新文件区域的内容取决于具体平台(在大多数系统上,额外的字节会填充为零)。 返回新的文件大小。在 3.5 版更改: 现在Windows在扩展时将文件填充为零。

  • close(): 刷新并关闭此流。如果文件已经关闭,则此方法无效。文件关闭后,对文件的任何操作(例如读取或写入)都会引发 ValueError 。为方便起见,允许多次调用此方法。但是,只有第一个调用才会生效。

  • closed:如果流已关闭,则返回 True。

复制一个文件的内容到另一个文件

with open(file_path, mode="r", encoding="utf-8") as f, open(
    file_path2, mode="w", encoding="utf8"
) as w_f:
    for line in f:
        print(line)
        w_f.write(line)

获取对应行的位置

with open(file_path, mode="r") as f:
    while True:
        line = f.readline()
        print(line)
        if not line:
            break
        print(f.tell())

替换一个文件中的部分内容

在文件中的部分内容周围添加内容

假设存在test.cpp,我们需要在#include前后添加相应的内容,替换内容均为add_test.

/**
 * @file test.cpp
 *
 */

#include <glog/logging.h>
int main(int argc, char *argv[])
{
    google::InitGoogleLogging(argv[0]);
    FLAGS_alsologtostderr = true;
    LOG(INFO) << "------conan test-----------------";

    google::ShutdownGoogleLogging();
    return 0;
}


import pathlib
from pathlib import Path

def content_before_add(file_path: Path, add_content: str):
    """add before content

    Args:
        file_path (Path): file to be operated
        add_content (str): What needs to be added
    """
    new_file_path = file_path.parent.joinpath(file_path.name + ".bak")
    with open(file_path, mode="r", encoding="utf-8") as r_f, open(
        new_file_path, mode="w", encoding="utf-8"
    ) as w_f:
        old_index = -1
        for line in r_f:
            w_f.write(line)
            if line.strip().startswith("#"):
                w_f.seek(old_index)
                w_f.write(add_content + "\n" + line)
            old_index = w_f.tell()
    new_file_path.replace(file_path)

def content_after_add(file_path: Path, add_content: str):
    """add after content

    Args:
        file_path (Path): file to be operated
        add_content (str): What needs to be added
    """
    new_file_path = file_path.parent.joinpath(file_path.name + ".bak")
    with open(file_path, mode="r", encoding="utf-8") as r_f, open(
        new_file_path, mode="w", encoding="utf-8"
    ) as w_f:
        for line in r_f:
            w_f.write(line)
            if line.strip().startswith("#"):
                w_f.write(add_content + "\n")
    new_file_path.replace(file_path)

替换后内容如下:

/**
 * @file test.cpp
 *
 */

add_test
#include <glog/logging.h>
int main(int argc, char *argv[])
{
    google::InitGoogleLogging(argv[0]);
    FLAGS_alsologtostderr = true;
    LOG(INFO) << "------conan test-----------------";

    google::ShutdownGoogleLogging();
    return 0;
}

大文件的读取

1.需要注意对于文件操作时候的模式,w默认会覆盖文件, a默认会追加文件中的内容。

python 官方文档 open

python官方文档 io