📊 阅读时长:20分钟 | 关键词:Python文件操作、open()、读写模式、with语句、os模块、路径处理

引言:和计算机关打交道的唯一方式

前面讲的都是"计算"——加减乘除、列表操作、函数调用。但程序真正强大之处,在于能和文件系统打交道:

  • 读取配置文件(.ini.json.yaml
  • 处理用户上传的文件(.csv.xlsx、图片)
  • 生成报表输出到本地(.txt.pdf.docx
  • 日志系统(.log 文件的读写)

Python 的文件操作 API 设计得非常优雅,学完这篇,你基本上能应付 90% 的文件处理任务。


一、路径操作:文件在哪?

在操作文件之前,你得先搞清楚文件在哪——这就是路径做的事。

1.1 绝对路径 vs 相对路径
类型 说明 示例(Windows) 示例(macOS/Linux)
绝对路径 从根目录开始的完整路径 D:\PythonFiles\p01.py /home/user/code/main.py
相对路径 相对于当前工作目录的路径 .\data\input.txt ./data/input.txt

获取当前工作目录

import os

cwd = os.getcwd()
print(cwd)   # 输出当前工作目录

相对路径的特殊符号

  • . → 当前目录
  • .. → 上一级目录
os.getcwd()                          # D:\PythonFiles
os.path.join('.', 'data', 'input.txt')  # .\data\input.txt
os.path.join('..', 'main.py')        # ..\main.py
1.2 os.path 常用方法速查
import os

path = r'D:\PythonFiles\data\input.txt'

os.path.basename(path)    # 'input.txt'       → 最后一级(文件名)
os.path.dirname(path)     # 'D:\PythonFiles\data'  → 目录部分
os.path.split(path)       # ('D:\...\data', 'input.txt')
os.path.splitext(path)  # ('D:\...\input', '.txt')  → 拆分扩展名
os.path.exists(path)      # False(不存在)
os.path.isfile(path)     # False
os.path.isdir(path)      # False

在这里插入图片描述

1.3 os.path.join():智能拼接路径(跨平台必备)

永远不要用字符串拼接路径! 不同操作系统的路径分隔符不同(Windows 用 \,macOS/Linux 用 /)。os.path.join() 会根据当前操作系统自动选择正确的分隔符。

import os

# ❌ 错误写法(不跨平台)
path = 'data' + '\\' + 'input.txt'   # 在 Mac 上会出错

# ✅ 正确写法
path = os.path.join('data', 'input.txt')
print(path)
# Windows → data\input.txt
# macOS/Linux → data/input.txt
1.4 创建目录:os.makedirs()
import os

# 创建单层目录
os.mkdir('new_folder')

# 递归创建多层目录(自动创建中间目录)
os.makedirs('./dir1/dir2/dir3', exist_ok=True)
# exist_ok=True:目录已存在时不报错

二、open():打开文件的万能钥匙

2.1 基本语法
file = open(file, mode='r', encoding=None)
参数 说明
file 文件路径(绝对或相对)
mode 打开模式,默认 'r'(只读)
encoding 文本文件编码,建议显式指定 'utf-8'

返回一个文件对象(迭代器对象),可以用 for 循环逐行读取。

2.2 文件打开模式详解
模式 说明 文件不存在时 文件已存在时
'r' 只读 报错 FileNotFoundError 正常打开,指针在开头
'w' 只写(覆盖) 创建新文件 清空原内容再写入
'a' 只写(追加) 创建新文件 指针在末尾,追加写入
'x' 独占创建 创建新文件 报错 FileExistsError
'b' 二进制模式 需配合 r/w/a 使用
'+' 读写模式 需配合 r/w/a 使用

组合模式示例

open('file.txt', 'rb')   # 二进制只读(如读取图片)
open('file.txt', 'wb')   # 二进制只写
open('file.txt', 'r+')  # 读写,指针在开头
open('file.txt', 'w+')  # 读写,清空后写入
open('file.txt', 'a+')  # 读写,指针在末尾
2.3 文本文件 vs 二进制文件
类型 编码 示例文件 打开模式
文本文件 需要指定 encoding .txt.py.json.md 'r'/'w'/'a'(默认)
二进制文件 无编码,直接读写字节 .png.mp3.exe.zip 'rb'/'wb'/'ab'

三、文件对象的常用方法

3.1 读取内容
with open('./exam.txt', 'r', encoding='utf-8') as f:
    # 方式1:next() 读取一行(文件是迭代器)
    print(next(f))

    # 方式2:for 循环逐行读取(最常用,内存友好)
    for line in f:
        print(line, end='')   # end='' 去掉多余的换行

    # 方式3:read(size) 读取指定字符数
    f.seek(0)               # 指针回到开头
    content = f.read(5)      # 读取前5个字符
    print(content)

    # 方式4:read() 读取全部
    f.seek(0)
    all_content = f.read()
    print(all_content)

    # 方式5:readlines() 读取全部行到列表
    f.seek(0)
    lines = f.readlines()
    print(lines)             # ['line1\n', 'line2\n', ...]

在这里插入图片描述

3.2 写入内容
with open('./exam.txt', 'w', encoding='utf-8') as f:
    # write(s):写入字符串,返回写入的字符数
    num = f.write('Hello World\n')
    print(f'写入了 {num} 个字符')

    # writelines(lines):写入字符串列表(不会自动加换行!)
    lines = ['Line 1\n', 'Line 2\n', 'Line 3\n']
    f.writelines(lines)

⚠️ 注意writelines() 不会自动添加换行符,需要自己加 \n

3.3 移动文件指针:seek(offset)
with open('./exam.txt', 'r+', encoding='utf-8') as f:
    f.write('Hello')
    f.seek(0)          # 指针回到开头
    print(f.read())       # 从头开始读取
3.4 刷新缓冲区:flush()

写入文件时,Python 会先把内容放到缓冲区,等缓冲区满了或文件关闭时才真正写入磁盘。flush() 可以强制立即写入。

import time

f = open('./test.txt', 'a', encoding='utf-8')
f.write('123456789\n')
f.flush()          # 立即写入磁盘(不等文件关闭)
time.sleep(5)     # 这5秒内文件里已经有内容了
f.close()

四、with 语句:最优雅的文件操作方式

4.1 为什么要用 with

传统写法需要手动关闭文件,如果中间发生异常,close() 可能执行不到:

# ❌ 不推荐的写法
f = open('./test.txt', 'w', encoding='utf-8')
f.write('hello world')
# 如果这里发生异常,close() 不会执行 → 文件损坏风险
f.close()

with 语句的好处:无论是否发生异常,退出 with 块时自动执行 close()

# ✅ 推荐写法
with open('./test.txt', 'w', encoding='utf-8') as f:
    f.write('hello world')
    # 退出 with 块时,自动调用 f.close()

等价于:

f = open('./test.txt', 'w', encoding='utf-8')
try:
    f.write('hello world')
finally:
    f.close()   # 无论如何都会执行
4.2 with + try/except/finally 组合
try:
    with open('./test.txt', 'r', encoding='utf-8') as f:
        content = f.read()
        print(content)
except FileNotFoundError:
    print('文件不存在!')
except UnicodeDecodeError:
    print('文件编码不是 UTF-8!')
finally:
    print('文件操作结束(自动关闭)')

五、实战:几个常用文件操作场景

5.1 复制文件
def copy_file(src, dst):
    """复制文件(支持文本和二进制)"""
    with open(src, 'rb') as fsrc:
        with open(dst, 'wb') as fdst:
            while True:
                chunk = fsrc.read(4096)   # 每次读 4KB
                if not chunk:
                    break
                fdst.write(chunk)
    print(f'已复制:{src}{dst}')

copy_file('./source.jpg', './backup/copy.jpg')
5.2 逐行处理日志文件(内存友好)
# 处理大文件时,绝对不能用 read() 一次性读入内存!
# 用 for 循环逐行读取,每次只占用一行的内存
error_count = 0
with open('./app.log', 'r', encoding='utf-8') as f:
    for line in f:                      # 逐行迭代,内存占用极低
        if 'ERROR' in line:
            error_count += 1
            print(f'发现错误:{line.strip()}')

print(f'共发现 {error_count} 处错误')
5.3 CSV 文件读写(数据分析必备)
import csv

# 读取 CSV
with open('./data.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    header = next(reader)    # 跳过表头
    print(f'表头:{header}')
    for row in reader:
        print(row)

# 写入 CSV
with open('./output.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '年龄', '城市'])      # 写表头
    writer.writerows([
        ['小明', 25, '北京'],
        ['小红', 23, '上海'],
    ])

六、动手练习

练习 1:统计文件行数

def count_lines(filepath):
    """统计文件行数"""
    with open(filepath, 'r', encoding='utf-8') as f:
        return sum(1 for _ in f)   # 逐行迭代计数

print(count_lines('./exam.txt'))

练习 2:查找包含关键词的行

def grep(filepath, keyword):
    """查找包含关键词的所有行"""
    with open(filepath, 'r', encoding='utf-8') as f:
        for i, line in enumerate(f, 1):
            if keyword in line:
                print(f'第 {i} 行:{line.strip()}')

grep('./app.log', 'ERROR')

练习 3:批量重命名文件

import os

def batch_rename(folder, old_ext, new_ext):
    """批量修改文件扩展名"""
    for filename in os.listdir(folder):
        if filename.endswith(old_ext):
            old_path = os.path.join(folder, filename)
            new_filename = filename.replace(old_ext, new_ext)
            new_path = os.path.join(folder, new_filename)
            os.rename(old_path, new_path)
            print(f'{filename}{new_filename}')

batch_rename('./images', '.jpeg', '.jpg')

小结

知识点 一句话总结
路径操作 os.path.join() 拼接路径,跨平台
open() 打开文件,返回文件对象;指定 modeencoding
读写模式 'r' 只读 / 'w' 覆盖 / 'a' 追加 / 'b' 二进制
读取方法 read() 全读 / readline() 读一行 / for line in f 逐行(推荐)
写入方法 write() 写字符串 / writelines() 写列表
with 语句 自动关闭文件,异常处理更安全,必须用
大文件处理 for line in f 逐行读取,不要用 read()

在这里插入图片描述


本文是「Python从入门到数据分析」系列的第 13 篇,共 24 篇。关注我,不错过后续更新。

更多推荐