Python 文件操作全面详解:从基础到进阶(附丰富实例)
文件操作是 Python 编程中最核心的技能之一,涵盖文件的创建、读写、关闭、删除、属性修改等场景。本文将系统梳理 Python 文件操作的核心概念、基础 API、进阶技巧和最佳实践,结合 20+ 个可直接运行的实例,从入门到精通掌握文件操作的方方面面。
一、文件操作核心概念
在开始代码实践前,先明确几个关键概念:
| 概念 | 含义 |
|---|---|
| 文件路径 | 文件在系统中的位置(绝对路径:如 C:/data/file.txt;相对路径:如 ./data/file.txt) |
| 文件模式 | 打开文件的方式(读、写、追加等,决定操作权限) |
| 文件对象 | 打开文件后返回的对象,用于后续读写操作(通过 open() 函数获取) |
| 缓冲区 | 内存中的临时区域,用于高效读写(默认开启,可手动控制) |
| 编码格式 | 文本文件的字符编码(如 UTF-8、GBK,决定能否正确读写中文等特殊字符) |
二、基础文件操作:open() 与文件对象
open() 函数是文件操作的入口,用于打开文件并返回文件对象。掌握它的参数和文件对象的方法,就能完成 80% 的日常任务。
2.1 open() 函数参数详解
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
| 核心参数 | 作用说明 | 常用取值 |
|---|---|---|
file |
必需,文件路径(字符串或 Path 对象) |
如 'data.txt'、Path('data/file.txt') |
mode |
打开模式(决定读写权限和文件类型) | 文本模式:'r'(读)、'w'(写)、'a'(追加)、'r+'(读写);二进制模式: 'rb'、'wb'、'ab'(加 b 表示二进制) |
encoding |
文本模式下的编码格式(二进制模式无需指定) | 'utf-8'(推荐,支持中文)、'gbk'(中文Windows默认) |
2.2 常用文件模式及权限
| 模式 | 含义 | 注意事项 |
|---|---|---|
'r' |
只读模式(默认),文件必须存在 | 文件不存在则报错 FileNotFoundError |
'w' |
只写模式,文件不存在则创建,存在则清空内容 | 谨慎使用,会覆盖原有内容 |
'a' |
追加模式,文件不存在则创建,写入内容附加到文件末尾 | 不会覆盖原有内容,适合日志记录 |
'r+' |
读写模式,文件必须存在,可读写内容 | 写入时从当前指针位置开始,可能覆盖原有内容 |
'w+' |
读写模式,文件不存在则创建,存在则清空 | 先清空再读写,慎用 |
'a+' |
追加+读模式,文件不存在则创建,写入到末尾,可读全文件 | 读文件需先移动指针(默认在末尾,直接读会返回空) |
2.3 基础操作流程:打开 → 操作 → 关闭
文件操作必须遵循“打开-操作-关闭”的流程,否则可能导致资源泄露。推荐用 with 语句(上下文管理器)自动关闭文件,无需手动调用 close()。
示例 1:文本文件读写(基础流程)
# 1. 写入文件('w' 模式)
with open('test.txt', 'w', encoding='utf-8') as f: # with 语句自动关闭文件f.write('Hello, Python!\n') # 写入字符串,\n 表示换行f.write('这是中文内容\n') # 需指定 encoding 才能正确写入中文# 2. 读取文件('r' 模式)
with open('test.txt', 'r', encoding='utf-8') as f:content = f.read() # 读取全部内容print('文件内容:\n', content)# 输出:
# 文件内容:
# Hello, Python!
# 这是中文内容
示例 2:二进制文件操作(如图片、音频)
二进制文件(非文本文件)需用 'rb'/'wb' 模式,且无需指定 encoding:
# 复制图片(二进制操作)
with open('source.jpg', 'rb') as src, open('copy.jpg', 'wb') as dst:data = src.read() # 读取二进制数据dst.write(data) # 写入二进制数据
print('图片复制完成')
三、文件读取的 5 种方式
根据需求不同,文件读取有多种方式,灵活选择可提高效率(尤其处理大文件时)。
| 方法 | 功能说明 | 适用场景 |
|---|---|---|
read() |
读取全部内容(可选参数指定字节数) | 小文件,需一次性处理全部内容 |
readline() |
读取一行内容(包括换行符 \n) |
按行处理文件(如日志、CSV) |
readlines() |
读取所有行,返回列表(每行作为元素) | 中等大小文件,需按行索引访问 |
| 迭代文件对象 | 直接用 for line in f 循环,逐行读取 |
大文件(内存友好,无需一次性加载全部内容) |
read(n) |
读取指定字节数的内容(二进制模式常用) | 分块处理大文件(如网络传输) |
示例 3:5 种读取方式对比
# 先创建测试文件
with open('read_demo.txt', 'w', encoding='utf-8') as f:f.write('第一行内容\n')f.write('第二行内容\n')f.write('第三行内容\n')# 1. read():读取全部内容
with open('read_demo.txt', 'r', encoding='utf-8') as f:print('1. read() 结果:\n', f.read())# 2. readline():逐行读取
with open('read_demo.txt', 'r', encoding='utf-8') as f:print('\n2. readline() 结果:')print(f.readline(), end='') # 第一行(end='' 避免重复换行)print(f.readline(), end='') # 第二行# 3. readlines():读取所有行到列表
with open('read_demo.txt', 'r', encoding='utf-8') as f:lines = f.readlines()print('\n3. readlines() 结果:', lines)print('按索引访问第二行:', lines[1])# 4. 迭代文件对象(最推荐的大文件处理方式)
with open('read_demo.txt', 'r', encoding='utf-8') as f:print('\n4. 迭代文件对象:')for line in f:print(f'行内容:{line}', end='')# 5. read(n):读取指定字节数(文本模式下约等于字符数,非精确)
with open('read_demo.txt', 'r', encoding='utf-8') as f:print('\n\n5. read(n) 结果:')print(f.read(5)) # 读取前5个字符print(f.read(3)) # 继续读取3个字符
输出结果:
1. read() 结果:第一行内容第二行内容第三行内容2. readline() 结果:
第一行内容
第二行内容3. readlines() 结果: ['第一行内容\n', '第二行内容\n', '第三行内容\n']
按索引访问第二行: 第二行内容4. 迭代文件对象:
行内容:第一行内容
行内容:第二行内容
行内容:第三行内容5. read(n) 结果:
第一行内
容
四、文件写入的 3 种方式
写入操作需注意模式选择('w' 覆盖 vs 'a' 追加),以及换行符的处理。
| 方法 | 功能说明 |
|---|---|
write(str) |
写入字符串,返回写入的字符数 |
writelines(seq) |
写入字符串序列(列表/元组等),不自动添加换行符 |
print(..., file=f) |
用 print 函数写入,可自动添加换行符(end 参数控制) |
示例 4:3 种写入方式对比
# 1. write():写入字符串
with open('write_demo.txt', 'w', encoding='utf-8') as f:count1 = f.write('用write写入第一行\n') # 需手动加换行符count2 = f.write('用write写入第二行')print(f'write写入字符数:{count1}, {count2}') # 输出:11, 9# 2. writelines():写入序列(无自动换行)
with open('write_demo.txt', 'a', encoding='utf-8') as f: # 'a' 追加模式lines = ['\n用writelines写入第三行', '用writelines写入第四行'] # 无换行符f.writelines(lines) # 不会自动加换行,内容会连在一起# 3. print() 函数写入(自动换行)
with open('write_demo.txt', 'a', encoding='utf-8') as f:print('用print写入第五行', file=f) # 自动加换行符print('用print写入第六行', file=f, end='---') # 自定义结尾符# 查看结果
with open('write_demo.txt', 'r', encoding='utf-8') as f:print('\n最终文件内容:\n', f.read())
输出结果:
write写入字符数:11, 9最终文件内容:用write写入第一行用write写入第二行用writelines写入第三行用writelines写入第四行用print写入第五行用print写入第六行---
五、文件指针与随机访问
文件对象内部有一个“指针”,记录当前读写位置。通过移动指针,可实现随机读写(如修改文件中间内容)。
| 方法/属性 | 功能说明 |
|---|---|
tell() |
返回当前指针位置(字节数) |
seek(offset, whence) |
移动指针到指定位置:offset 偏移量(正数向后,负数向前);whence 基准位置(0:文件开头,1:当前位置,2:文件末尾) |
示例 5:文件指针操作
with open('pointer_demo.txt', 'w+', encoding='utf-8') as f: # 'w+' 读写模式f.write('0123456789') # 写入10个字符print('写入后指针位置:', f.tell()) # 输出:10(在文件末尾)# 移动指针到开头( whence=0 表示从文件开头计算)f.seek(0, 0)print('移动到开头后读取:', f.read(3)) # 读取前3个字符:012print('当前指针位置:', f.tell()) # 输出:3# 从当前位置向后移动2个字符f.seek(2, 1) # whence=1 表示从当前位置计算print('移动后读取:', f.read(2)) # 读取位置5-6的字符:56print('当前指针位置:', f.tell()) # 输出:7# 从文件末尾向前移动3个字符f.seek(-3, 2) # whence=2 表示从文件末尾计算print('从末尾移动后读取:', f.read()) # 读取最后3个字符:789
输出结果:
写入后指针位置: 10
移动到开头后读取: 012
当前指针位置: 3
移动后读取: 56
当前指针位置: 7
从末尾移动后读取: 789
六、文件属性与元数据
通过 os 或 pathlib 库可获取文件的元数据(大小、创建时间、修改时间等)。
示例 6:获取文件属性
from pathlib import Path
import timefile_path = Path('test.txt')# 确保文件存在
if not file_path.exists():with open(file_path, 'w') as f:f.write('测试文件属性')# 获取基本属性
print('文件是否存在:', file_path.exists()) # True
print('是否为文件:', file_path.is_file()) # True
print('文件大小(字节):', file_path.stat().st_size) # 12(中文字符在UTF-8中占3字节,共4个汉字=12字节)# 获取时间属性(时间戳需转换为可读格式)
create_time = file_path.stat().st_ctime # 创建时间(Windows)/ 首次修改时间(Unix)
modify_time = file_path.stat().st_mtime # 最后修改时间
print('创建时间:', time.ctime(create_time))
print('最后修改时间:', time.ctime(modify_time))# 输出示例:
# 文件是否存在: True
# 是否为文件: True
# 文件大小(字节): 12
# 创建时间: Wed Jun 12 10:00:00 2024
# 最后修改时间: Wed Jun 12 10:00:00 2024
七、进阶操作:批量处理与异常处理
实际开发中,文件操作常需处理批量文件和异常情况(如文件不存在、权限不足)。
7.1 批量处理文件(结合 glob 或 pathlib)
示例 7:批量读取目录下所有 .txt 文件
from pathlib import Path# 获取当前目录下所有 .txt 文件
txt_files = list(Path('.').glob('*.txt')) # 返回 Path 对象列表
print('找到的txt文件:', [f.name for f in txt_files])# 批量读取每个文件的前50字符
for file in txt_files:with open(file, 'r', encoding='utf-8', errors='ignore') as f: # errors='ignore' 忽略编码错误content = f.read(50) # 读取前50字符print(f'\n{file.name} 内容预览:{content}...')
7.2 异常处理(避免程序崩溃)
文件操作可能出现多种异常(如 FileNotFoundError、PermissionError),需用 try-except 捕获。
示例 8:文件操作异常处理
file_path = 'nonexistent.txt' # 不存在的文件try:with open(file_path, 'r', encoding='utf-8') as f:content = f.read()
except FileNotFoundError:print(f'错误:文件 {file_path} 不存在')
except PermissionError:print(f'错误:没有权限访问 {file_path}')
except UnicodeDecodeError:print(f'错误:{file_path} 编码错误,无法读取')
except Exception as e: # 捕获其他未知异常print(f'发生未知错误:{e}')
else: # 无异常时执行print(f'成功读取 {file_path},内容长度:{len(content)}')
finally: # 无论是否有异常都执行(如清理资源)print('操作结束')# 输出:
# 错误:文件 nonexistent.txt 不存在
# 操作结束
7.3 大文件处理(避免内存溢出)
处理 GB 级大文件时,不能用 read() 一次性加载,需逐行或分块读取。
示例 9:分块读取大文件(如日志分析)
def process_large_file(file_path, block_size=1024*1024): # 1MB 块with open(file_path, 'r', encoding='utf-8') as f:while True:block = f.read(block_size) # 每次读取1MBif not block: # 读取完毕break# 处理块内容(示例:统计包含"error"的行数)error_count = block.count('error') + block.count('ERROR')print(f'当前块错误数:{error_count}')# 模拟大文件(实际使用时替换为真实大文件路径)
# process_large_file('large_log.txt')
print('大文件处理逻辑:分块读取,逐块处理')
八、常见场景实战
场景 1:CSV 文件读写(模拟数据处理)
# 写入 CSV 数据
with open('data.csv', 'w', encoding='utf-8', newline='') as f: # newline='' 避免多余空行f.write('姓名,年龄,城市\n')f.write('张三,25,北京\n')f.write('李四,30,上海\n')# 读取并解析 CSV
with open('data.csv', 'r', encoding='utf-8') as f:header = f.readline().strip().split(',') # 表头print('表头:', header)for line in f:data = line.strip().split(',')print(f'数据:{data[0]},{data[1]}岁,来自{data[2]}')# 输出:
# 表头: ['姓名', '年龄', '城市']
# 数据:张三,25岁,来自北京
# 数据:李四,30岁,来自上海
场景 2:日志文件追加(记录程序运行日志)
import datetimedef write_log(message, log_file='app.log'):"""写入日志,包含时间戳"""timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')log_line = f'[{timestamp}] {message}\n'with open(log_file, 'a', encoding='utf-8') as f: # 'a' 追加模式f.write(log_line)# 模拟程序运行日志
write_log('程序启动')
write_log('用户登录:admin')
write_log('警告:内存使用率超过80%')
write_log('程序退出')# 查看日志
with open('app.log', 'r', encoding='utf-8') as f:print('日志内容:\n', f.read())
输出结果:
日志内容:[2024-06-12 10:30:00] 程序启动[2024-06-12 10:30:05] 用户登录:admin[2024-06-12 10:30:10] 警告:内存使用率超过80%[2024-06-12 10:30:15] 程序退出
场景 3:文件加密/解密(简单替换加密)
def encrypt_file(src_path, dst_path, key=3):"""简单凯撒加密:每个字符ASCII码+key"""with open(src_path, 'r', encoding='utf-8') as src, open(dst_path, 'w', encoding='utf-8') as dst:for line in src:encrypted = ''.join([chr(ord(c) + key) for c in line])dst.write(encrypted)def decrypt_file(src_path, dst_path, key=3):"""解密:每个字符ASCII码-key"""with open(src_path, 'r', encoding='utf-8') as src, open(dst_path, 'w', encoding='utf-8') as dst:for line in src:decrypted = ''.join([chr(ord(c) - key) for c in line])dst.write(decrypted)# 测试加密解密
with open('secret.txt', 'w', encoding='utf-8') as f:f.write('Hello, 世界!')encrypt_file('secret.txt', 'encrypted.txt')
decrypt_file('encrypted.txt', 'decrypted.txt')# 验证结果
with open('decrypted.txt', 'r', encoding='utf-8') as f:print('解密后内容:', f.read()) # 输出:Hello, 世界!
九、最佳实践与避坑指南
-
始终用
with语句打开文件:自动管理资源,避免忘记close()导致的文件占用。 -
指定编码格式:文本文件操作时显式指定
encoding='utf-8',避免中文乱码(尤其是 Windows 系统默认 GBK 编码)。 -
区分文本与二进制模式:
- 文本模式(
'r'/'w'):用于.txt、.csv等,读写字符串,需编码; - 二进制模式(
'rb'/'wb'):用于图片、音频、压缩包等,读写字节(bytes),无需编码。
- 文本模式(
-
处理大文件用迭代或分块:避免
read()一次性加载全部内容,防止内存溢出。 -
异常处理不可少:至少捕获
FileNotFoundError(文件不存在)和PermissionError(权限不足)。 -
谨慎使用
'w'模式:会清空原有文件,重要数据操作前先备份。 -
路径处理用
pathlib:跨平台兼容,避免手动拼接路径(如'dir' + '/' + 'file.txt')。
十、总结
Python 文件操作核心围绕 open() 函数和文件对象展开,掌握以下几点即可应对绝大多数场景:
- 基础读写:
read()/write()+with语句; - 模式选择:根据需求选
'r'/'w'/'a'及二进制模式; - 高效处理:大文件用迭代,批量文件用
glob/pathlib; - 安全保障:异常处理 + 显式编码 + 谨慎模式。
通过本文的实例练习,可快速掌握从简单文本读写到复杂批量处理的全流程技能,为数据处理、日志分析、配置管理等任务打下坚实基础。