当前位置:首页>python>Python之文件写操作

Python之文件写操作

  • 2026-09-08 21:01:17
Python之文件写操作

上一篇我们已经介绍了文件读操作,这一篇我们重点介绍文件写操作。

第十章 Python之文件写操作

类别
选项
含义
读写主模式
r
只读
(默认)。文件必须存在,否则报错 FileNotFoundError。
w
只写
。文件存在则清空(截断),不存在则新建。危险操作,注意备份!
x
独占创建
。写模式,但文件已存在则抛出 FileExistsError,防止覆盖。
a
追加
。文件存在则末尾追加,不存在则新建。无法读取,只能写。
格式模式
t
文本模式
(默认)。读写字符串 str,需指定 encoding。
b
二进制模式
。读写字节流 bytes(如图片、音频),禁止传 encoding 参数。
附加标识
+
更新模式
(读写兼得)。搭配使用:r+(读写,指针在开头)、w+(清空读写)、a+(追加读写,指针在末尾)。

1. 写入文件

Python中写入文本文件有三种常用模式,区别在于如何处理已存在的文件和写入位置。

1.1 w模式(写入,覆盖)

  • 行为:打开文件用于写入。若文件已存在,会先清空(截断)整个文件;若不存在则新建。
  • 注意:每次打开都会丢失原有内容,适合从头生成新内容。
# open 函数最常用的三个参数如下:
#   1.file:要操作的文件路径
#   2.mode:文件的打开模式
#       r :读取(默认值)
#       w :写入,并先截断文件
#       x :排它性创建,如果文件已存在,则创建失败
#       a :打开文件用于写入,如果文件存在,则在文件末尾追加内容
#       b :二进制模式
#       t :文本模式(默认值)
#       + :打开用于更新(读取与写入)
#   3.encoding:字符编码

# 测试 w 模式
with open('demo.txt', 'wt', encoding='utf-8') as file:
    file.write('Hello Python!')

💡 频繁使用 w 时,务必确认文件路径,避免误清空重要数据。

1.2 x模式(排他创建,安全写入)

  • 行为:仅当文件不存在时才创建并写入;若文件已存在,立即抛出 FileExistsError,避免意外覆盖。
  • 适用场景:日志归档、配置文件初始化等确保不覆盖已有文件的需求。
# 测试 x 模式
with open('demo2.txt', 'xt', encoding='utf-8') as file:
    file.write('Hello Python!')

⚠️ 需要配合异常处理(try-except)来优雅处理文件已存在的情况。

1.3 a 模式(追加,保留历史)

  • 行为:打开文件用于写入,文件指针自动移至末尾。若文件存在,新内容追加在最后;若不存在则新建。
  • 适用场景:日志记录、持续收集数据,希望保留所有历史写入。
# 测试 a 模式
with open('demo2.txt', 'at', encoding='utf-8') as file:
    file.write('Hello Python!'

🔄 a 模式不会清空文件,多次写入的内容会按顺序累积。

1.4  快速对照表

模式
文件不存在
文件存在
写入位置
w
新建
清空后写入
开头
x
新建
报错
开头
a
新建
末尾追加
末尾

选型建议:

  • 需要覆盖重写 → w
  • 需要防止误覆盖 → x(配合 try)
  • 需要保留旧记录 → a

所有模式都建议使用 with 语句,确保文件自动关闭,避免资源泄漏。若操作二进制文件(如图片),将模式中的 t 改为 b(如 'wb'、'xb'、'ab')。

1.5 flush 方法(强制落盘)

  • 概述:Python 写入文件时,为提升性能,不会每次调用 write() 都直接操作硬盘,而是先将数据积攒在内存的缓冲区中,待缓冲区满或文件关闭时才统一写入。
  • flush() 作用:立即将缓冲区中积攒的所有数据强制写入磁盘文件,不等缓冲区满或文件关闭。
# 在 Python 中文件写入时,并不是每写一次就立刻落盘,而是:先写到“缓冲区”里。
# 文件对象的 flush 方法:把缓冲区中的数据,立刻写入到文件中。
import time
with open('demo.txt', 'at', encoding='utf-8') as file:
    file.write('张三')
    file.write('李四')   # 此时还在缓冲区,未落盘
    file.flush()          # 强制落盘,此时文件中已存在"张三李四"
    time.sleep(10000)     # 长眠期,打开文件可见前两句,但看不到后两句
    file.write('王五')
    file.write('赵六')   # 程序结束或缓冲区满时,这两句才会真正写入

💡 在 sleep 期间查看 demo.txt,你会看到前两句已保存,而后两句仍在内存中——这就是 flush 的即时生效效果。

🧠 核心补充:

  • 自动刷新时机:文件被 close() 关闭时,会自动调用 flush(),因此正常情况下无需手动调用。
  • 何时需要手动flush:
    • 写实时日志,希望崩溃前也能保留关键记录;
    • 进度提示或交互式写入,需要用户立即看到输出;
    • 防止程序异常退出导致缓冲区数据丢失。
  • 性能权衡:flush() 会触发磁盘 I/O,频繁调用会显著降低写入速度,建议仅在关键节点使用。

⚠️ 与 close() 的关系:

  • close() 内部已包含 flush(),所以关闭后无需再单独调用。如果代码中使用了 with 语句,退出上下文时会自动关闭并刷新,通常无需额外操作。

2. 组合模式

+ 表示可读可写,但具体行为(指针位置、是否清空)仍由基础模式(r / w / x / a)决定。t 为文本默认值,可省略(如 'r+' 即 'rt+')。

2.1  rt+(读写,从头开始)

  • 行为:文件必须存在。指针初始在开头,写入会从当前位置覆盖字符。
  • 注意:写入后若需读取刚写的内容,必须先用 seek 移回开头;文本模式下seek偏移量请谨慎使用(中文字符占多字节,随意定位会破坏编码)。
# 测试 rt+
with open('a.txt', 'rt+', encoding='utf-8') as file:
# seek(offset, whence)方法:用于改变文件对象指针的位置,参数说明如下:
#   offset:偏移量,要移动多少距离
#   whence:参考点,从哪里开始计算偏移,有三种取值:
#       0:从文件开头计算(默认值)
#       1:从当前位置计算
#       2:从文件末尾计算
#  注意:在文本模式下,不要随意去定位中文字符位置,否则可能破坏文件编码。
    file.seek(0, 0)
    file.write('你好')

2.2 wt+(读写,先清空)

  • 行为:文件存在则清空(截断),不存在则新建。指针初始在开头。w模式可以写入,+模式可以用于更新(读取或写入),所以wt+模式可读可写。
  • 关键点:执行 write 后,指针会移动到末尾。想读取刚写入的内容,必须先 seek(0) 重置指针。w模式打开文件后,文件指针在起始位置,但write方法执行完后,指针在文件结束位置。由于t是默认值,所以wt+中的t可以省略。
with open('a.txt', 'wt+', encoding='utf-8') as file:
    file.write('你好')    # 写入后指针在末尾
    file.seek(0)          # 必须移回开头才能读取
    result = file.read()
    print(result)

2.3 xt+(读写,排他创建)

  • 行为:文件不存在则创建并允许读写;若已存在则报错 FileExistsError。指针初始在开头。x模式打开文件后,文件指针在起始位置。
  • 适用:安全创建新文件,避免误覆盖现有数据。
with open('demo3.txt', 'xt+', encoding='utf-8') as file:
    file.write('你好')
    file.seek(0)          # 移回开头再读取
    result = file.read()
    print(result)

2.4 at+(读写,强制追加)

  • 行为:文件存在则指针初始在末尾,不存在则新建。调用 write 永远追加到文件末尾,即使你手动用 seek 移动了指针,写入时依然会强制跳回末尾。at模式可以追加内容,+模式可以用于更新(读取或写入),所以at+模式可读可写。at模式打开文件后,文件指针在结束位置。
with open('a.txt', 'at+', encoding='utf-8') as file:
    file.write('你好')    # 默认追加到末尾
    file.seek(0)          # 必须移回开头才能读取已有全文
    result = file.read()
    print(result)

🔥 避坑:at+ 模式下,seek 只能影响读取位置,对写入位置无效(写入永远追加)。因此若想在文件中间修改内容,请改用 r+。

快速对比(关键差异):

模式
文件不存在
文件存在时操作
初始指针
写入后指针
r+
报错
从头覆盖(不清空)
开头
末尾
w+
新建
清空再写入
开头
末尾
x+
新建
报错(安全防覆盖)
开头
末尾
a+
新建
强制末尾追加
末尾
末尾(写入位置不变)

选型建议:

  • 需要修改已有内容(定位改写)→ r+
  • 需要覆盖重写并立即验证 → w+
  • 需要安全新增文件并检查内容 → x+
  • 需要追加记录并同时查看全部历史 → a+

⚠️ 读写混合模式容易混淆,牢记写后读必 seek(0)口诀,避免读到空数据或乱码。

3. 目录操作(os / shutil)

Python 通过 os 和 shutil 模块提供目录管理能力,核心分为创建、删除、判断和遍历四类。

3.1 创建目录

  • os.mkdir(path):创建单级目录。若父目录不存在或目录已存在,抛出 FileNotFoundError / FileExistsError。
  • os.makedirs(path):创建多级目录(自动创建缺失的父级目录)。若全部已存在则报错。
import os
os.mkdir('D:/demo')                # 仅一级
os.makedirs('D:/demo/aa/bb')       # 自动创建 demo → aa → bb

💡 makedirs 还可传 exist_ok=True(Python 3.2+)使其在目录已存在时不报错,更安全。

3.2 删除目录

  • os.rmdir(path):删除空目录。目录非空或不存在均报错。
  • os.removedirs(path):递归删除空目录。从最末一级开始向上删除,直到遇到非空目录即停止。
  • shutil.rmtree(path):强制删除整个目录树(含所有子目录和文件),危险操作,谨慎使用。
os.rmdir('D:/demo/aa/bb')          # 删除 bb(必须为空)
os.removedirs('D:/demo/aa/bb')     # 先删 bb,若 aa 变空则继续删 aa,依此类推
shutil.rmtree('D:/demo')           # 直接删除整个 demo,不可恢复

3.3 路径判断(路径存在性 & 类型)

函数
返回 True 的条件
os.path.exists(path)
路径存在(文件或目录均可)
os.path.isdir(path)
路径存在 且 是目录
os.path.isfile(path)
路径存在 且 是文件

python

print(os.path.exists('D:/demo/aa/bb'))   # False(若不存在)
print(os.path.isdir('D:/demo'))          # True(若为目录)
print(os.path.isfile('D:/demo'))         # False(目录不是文件)

3.4 遍历目录(轻量 & 递归)

  • os.scandir(path):返回迭代器,遍历单层目录项。比 os.listdir 更高效(直接获取文件类型信息),适合一层扫描。
with os.scandir('D:/demo') as entries:   # 建议用 with 管理资源
for entry in entries:
        print('目录'if entry.is_dir() else'文件', entry.name)
  • os.walk(path):递归生成三元组 (当前目录路径, 子目录列表, 文件名列表),按层级逐层遍历整个目录树。
for dirpath, dirnames, filenames in os.walk('D:/demo'):
    print(f'当前目录:{dirpath}')
    print(f'子目录:{dirnames}')
    print(f'文件:{filenames}')

⚠️危险操作提醒:

  • shutil.rmtree 直接删除整个目录树,·无回收站·,执行前务必确认路径(建议先用 os.path.exists 检查)。
  • 若需删除非空目录更安全,可先遍历删除文件,再删空目录,但 rmtree 仍是首选(注意备份)。
# ⚠️危险操作:删除有内容的目录
# shutil.rmtree('D:/demo')

📌 场景选型速查:

  • 新建单目录 → mkdir
  • 新建嵌套目录 → makedirs
  • 删空目录 → rmdir 或 removedirs
  • 强制删除全目录 → shutil.rmtree
  • 判断路径类型 → exists / isdir / isfile
  • 单层遍历 → scandir
  • 全递归遍历 → walk

4. 综合小练习

练习1:将一个二进制文件复制到指定位置。

# 练习1:将一个二进制文件复制到指定位置。
# region
import os
# 源文件
source = 'music.mp3'
# 目标目录
target = 'D:/media'

# 如果目标目录不存在,那就去创建
if not os.path.isdir(target):
    os.makedirs(target)

with open(source, 'rb') as f1, open(target + '/' + 'my_music.mp3', 'wb') as f2:
    while True:
        # 每次只读取1KB
        data = f1.read(1024)
        # 如果文件读取完毕了,就跳出循环
        if not data:
            break
        # 向目标文件中写入数据
        f2.write(data)
    print('复制完毕')
# endregion

练习 2:日志记录,需求如下:

  1. 用户输入用户名和密码后,程序进行校验:
  2. 用户名不存在,提示“用户名未注册”,并记录日志。
  3. 用户名存在,但密码错误,提示“密码错误”,并记录日志。
  4. 用户名和密码均正确,提示“登录成功”,并记录日志。
# 练习2:日志记录。
#   1.用户输入用户名和密码后,程序进行校验:
#   2.用户名不存在,提示“用户名未注册”,并记录日志。
#   3.用户名存在,但密码错误,提示“密码错误”,并记录日志。
#   4.用户名和密码均正确,提示“登录成功”,并记录日志。

import time
# 准备一些用户
users = {
    '张三': '123456',
    '李四': '888888',
    '王五': 'abc123'
}

# 提示输入信息
username = input('请输入用户名:')
password = input('请输入密码:')

# 获取当前的时间
now = time.strftime('%Y-%m-%d %H:%M:%S')

# 如果用户名不在users中
if username notin users:
    print('用户名未注册')
    with open('log.txt', 'at', encoding='utf-8') as file:
        file.write(f'{now} {username}  登录失败(用户未注册)\n')

# 如果密码不正确
elif users[username] != password:
    print('密码不正确')
    with open('log.txt', 'at', encoding='utf-8') as file:
        file.write(f'{now} {username}  密码错误 \n')

# 登录成功
else:
    print('登录成功!')
    with open('log.txt', 'at', encoding='utf-8') as file:
        file.write(f'{now} {username}  登录成功 \n')

最新文章

随机文章