文件读写

作为Python日常开发中最常见的IO场景之一,文件读写看似简单,细节却能决定代码的健壮性、效率甚至安全性——今天咱们就用一篇短文,把它讲明白,从基础API到避坑指南全搞定!

文件操作基础

Python 提供了内置的 open() 函数完成核心的文件操作,它返回一个文件对象/文件描述符——这是连接Python代码和操作系统磁盘文件的“桥梁”。

现代操作系统为了安全性和稳定性,禁止程序直接读写磁盘扇区,所有文件操作都必须经过它提供的统一API包装,这也是为什么我们不能直接用内存地址读取文件的原因。


读取文件

基础打开与异常处理

open() 读取文件的默认模式是 'r'(文本读取),但如果路径错误或文件不存在,Python 会直接抛出 FileNotFoundError 打断程序:

# 基础读取打开
f = open('/path/to/your/file.txt', 'r')

为了让程序更健壮,务必加上基础异常捕获:

try:
    f = open('/path/to/nonexistent.txt', 'r')
except FileNotFoundError as e:
    print(f"文件不存在,请检查路径:{e}")

四种常用读取内容的方式

成功打开后,根据文件大小、读取需求,选择对应的方法:

  1. 一次性全读:适合小文件(比如几十KB以内)
    content = f.read()  # 读取整个文件到单个字符串
  2. 按字节/字符分段读:大文件友好,避免一次性加载占满内存
    chunk = f.read(1024)  # 文本模式读1024个字符,二进制读1024字节
  3. 逐行单读:需要根据每行逻辑做处理时常用
    line = f.readline()  # 读取一行,包含末尾的换行符\n
  4. 全读进列表:后续要多次操作行内容时方便
    lines = f.readlines()  # 每一行(含\n)作为列表的一个元素

安全释放资源的两种方法

文件打开后会占用操作系统的文件描述符资源(每个进程的数量有限,比如Linux默认1024个),用完必须关闭!

  1. 手动关闭:容易忘,出错(比如读写抛异常)时会跳过 close()
    f = open('/path/to/file.txt', 'r')
    content = f.read()
    f.close()  # 必须放在try/except的finally块里才绝对安全
  2. with 自动管理(强烈推荐):利用上下文管理器,代码块结束时自动触发 close(),哪怕中间抛异常
    with open('/path/to/file.txt', 'r') as f:
        content = f.read()
    # 这里已经自动关闭,f变量失效

文件对象类型

Python 支持三类核心的“文件-like”对象:

  1. 磁盘文本文件:默认打开方式,按字符/字符串处理,会自动解码/编码
    with open('note.txt', 'rt') as f:  # 't'可省略
        pass
  2. 磁盘二进制文件:处理图片、视频、压缩包等非文本内容,按字节/字节串处理
    with open('cat.jpg', 'rb') as f:
        img_bytes = f.read()
  3. 内存文件:完全在内存中模拟文件操作,无需磁盘IO,速度极快,适合临时数据
    from io import StringIO, BytesIO
    
    # 文本内存流
    text_stream = StringIO("临时的博客草稿")
    text_stream.write("\n再加一行修改")
    print(text_stream.getvalue())  # 不用with也能直接取所有内容
    text_stream.close()
    
    # 二进制内存流
    binary_stream = BytesIO(b'\x48\x65\x6c\x6c\x6f')  # 对应"Hello"

处理不同编码的文本文件

默认Python 3用 UTF-8 编码读写文本,但Windows上经常遇到GBK/GB2312的旧文件,直接读会报 UnicodeDecodeError

# 指定编码读取GBK文件
with open('old_chinese_note.txt', 'r', encoding='gbk') as f:
    content = f.read()

如果文件有少量乱码(比如非法UTF-8字符),可以用 errors 参数调整策略:

  • errors='ignore':忽略非法字符
  • errors='replace':用 替换非法字符
  • errors='strict':默认,直接报错(最安全,提醒你文件有问题)
# 遇到乱码就替换
with open('mixed_encoding.txt', 'r', encoding='utf-8', errors='replace') as f:
    content = f.read()

写入文件

两种基础写入模式

写入操作的核心是避免误覆盖数据,先看两种最常用的模式:

  1. 覆盖写入('w':如果文件不存在会自动创建,存在则清空所有内容重新写
    with open('new_diary.txt', 'w', encoding='utf-8') as f:
        f.write("今天学了Python文件读写!")
  2. 追加写入('a':文件不存在自动创建,存在则从文件末尾开始写
    with open('new_diary.txt', 'a', encoding='utf-8') as f:
        f.write("\n明天继续学CSV/JSON读写!")

写入多行的两种方式

直接循环调用 f.write() 或者用更简洁的 f.writelines()

lines = ["第一行", "第二行", "第三行"]

# 方式1:手动加换行符循环写
with open('multi_line.txt', 'w', encoding='utf-8') as f:
    for line in lines:
        f.write(f"{line}\n")

# 方式2:用生成器表达式批量传值(更Pythonic)
with open('multi_line.txt', 'w', encoding='utf-8') as f:
    f.writelines(f"{line}\n" for line in lines)

⚠️ 注意f.writelines() 不会自动加换行符!必须自己手动拼接。


文件模式速查表

基础模式核心功能组合后缀补充说明
'r'只读(默认)'t'文本模式(默认,可省略)
'w'只写(清空覆盖,自动创建)'b'二进制模式
'x'独占创建(文件存在则报错)'+'可同时读写(基础模式功能保留)
'a'只写(末尾追加,自动创建)

常见组合示例:

  • 'rb':读取图片、压缩包等二进制文件
  • 'w+':读写(覆盖式,读完要手动移动文件指针才能重读)
  • 'a+':读写(追加式,读完同样要移动指针)

避坑与最佳实践

  1. 永远默认加 encoding='utf-8':避免跨平台(Windows→Linux/macOS)时的编码问题
  2. 永远用 with 语句:忘记手动关闭文件是新手最容易犯的低级错误
  3. 大文件必须逐行/分段迭代:比如GB级的日志文件,用 for line in f:f.readlines() 内存友好100倍
    with open('10GB_server.log', 'r', encoding='utf-8') as f:
        for line in f:  # 迭代文件对象默认是逐行读
            if "ERROR" in line:
                print(line.strip())
  4. 'x' 模式避免覆盖关键数据:比如写用户配置文件时,防止不小心把旧配置删了
    try:
        with open('user_config.json', 'x', encoding='utf-8') as f:
            f.write('{"theme": "dark"}')
    except FileExistsError:
        print("配置文件已存在,无需创建")

快速练习:读取系统时区文件

用刚学的知识写个小脚本,读取Linux/macOS常用的时区文件:

import os

TIMEZONE_PATH = '/etc/timezone'

if __name__ == '__main__':
    if not os.path.exists(TIMEZONE_PATH):
        print("当前系统没有找到 /etc/timezone 文件(可能是Windows)")
        exit(1)
    
    try:
        with open(TIMEZONE_PATH, 'r', encoding='utf-8') as f:
            print(f"当前系统时区:{f.read().strip()}")  # strip()去掉末尾的换行
    except PermissionError:
        print("没有权限读取 /etc/timezone 文件,请用sudo运行")

总结

今天我们梳理了Python文件读写的核心知识点:

  1. open()with 上下文管理器安全打开/关闭文件
  2. 区分文本/二进制/内存文件的使用场景
  3. 处理编码问题的方法
  4. 覆盖/追加/独占创建三种基础写入模式
  5. 大文件处理的内存友好技巧

掌握这些,日常开发中的90%文件IO需求都能轻松搞定!下一篇我们聊聊更高级的 CSV/JSON/Excel 结构化文件读写~