在Python开发中,文件读写是绕不开的基础技能。无论是解析配置文件、处理日志,还是持久化程序输出,都需要依赖open()函数和文件对象。本文从实际使用角度出发,系统梳理文件打开模式、读取/写入方法、指针定位、with语句的正确用法,以及大文件处理和异常捕获的常见场景,帮助你写出更健壮的文件操作代码。
一、open()函数基础
所有文件操作都从open()开始,其基本语法为:
- file_object = open(file_path, mode='r', encoding=None)
复制代码
三个关键参数:
- file_path:文件路径,支持相对路径或绝对路径。
- mode:打开模式,决定读写方式和是否追加。
- encoding:文本编码,处理中文时务必指定'utf-8',否则可能因平台默认编码不同导致乱码或解码错误。
需要特别提醒:手动调用open()后必须用close()关闭文件,否则会占用文件描述符,长期运行可能资源耗尽。更推荐的方式是使用with语句,后文会详细说明。
二、文件打开模式详解
mode参数决定了文件的操作方式,常用模式如下:
- 'r':只读模式(默认),文件必须存在,否则抛出FileNotFoundError。
- 'w':只写模式,会创建新文件或清空已有文件内容。
- 'a':追加模式,在文件末尾写入,保留原有内容,文件不存在时自动创建。
- 'r+':读写模式,文件指针在开头,可读可写,文件必须存在。
- 'w+':读写模式,会清空或创建文件,然后可读可写。
- 'a+':读写模式,文件指针在末尾,写入始终追加,可读但需要配合seek移动指针。
此外,处理图片、视频、压缩包等二进制文件时,需要在模式后加'b',例如'rb'、'wb'。文本模式默认会做换行符转换,二进制模式则不做任何转换。
三、读取文件内容
读取文件有多种方式,应根据文件大小和需求选择。
1. 一次性读取全部内容:- with open('example.txt', 'r', encoding='utf-8') as f:
- content = f.read()
- print(content)
复制代码
适合小文件,能快速拿到完整字符串。
2. 逐行读取:- with open('example.txt', 'r', encoding='utf-8') as f:
- for line in f:
- print(line, end='') # line已包含换行符,end=''避免重复换行
复制代码
这是处理文本文件的推荐方式,内存占用低。
3. 读取所有行到列表:- with open('example.txt', 'r', encoding='utf-8') as f:
- lines = f.readlines()
- for line in lines:
- print(f"Line: {line.strip()}")
复制代码
readlines()返回每行字符串组成的列表,适合需要随机访问行号的场景。
4. 读取指定字节数或字符数:- with open('example.txt', 'r', encoding='utf-8') as f:
- chunk = f.read(100) # 读取100个字符
- print(chunk)
复制代码
read(size)在文本模式下表示字符数,在二进制模式下表示字节数。
四、写入文件内容
写入文件同样有字符串和列表两种常用方式。
1. 写入字符串:- with open('output.txt', 'w', encoding='utf-8') as f:
- f.write('Hello, World!\n')
- f.write('这是第二行。')
复制代码
注意write()不会自动添加换行符,需要手动写入'\n'。
2. 写入多行(列表):- lines_to_write = ['第一行\n', '第二行\n', '第三行\n']
- with open('output.txt', 'w', encoding='utf-8') as f:
- f.writelines(lines_to_write)
复制代码
writelines()的参数是字符串迭代器,不会自动添加换行符,列表中的字符串必须自己包含换行。
五、文件指针与定位
文件对象内部维护一个指针,标识当前读写位置。通过tell()获取当前位置,seek()移动指针。
- with open('example.txt', 'r+', encoding='utf-8') as f:
- print(f.tell()) # 当前指针位置,通常是0
- content = f.read(10)
- print(content)
- f.seek(0) # 指针移回文件开头
- f.write('覆盖开头') # 从开头写入,覆盖原内容
复制代码
在'r+'模式下,seek(0)后写入会覆盖原位置内容,而不是插入。在'a+'模式下,写入始终在末尾,即使移动指针也无法改变追加行为。
六、with语句与上下文管理器
with open(...) as f是Python处理文件的最佳实践,它基于上下文管理器协议,能够自动关闭文件,避免资源泄漏。
执行原理:open()返回的文件对象实现了__enter__和__exit__方法。进入with块时调用__enter__,返回值赋给as后的变量;离开with块时(无论正常结束还是异常)调用__exit__,内部执行close()。
推荐使用with而不是手动open/close,原因包括:
- 避免忘记close()导致文件描述符耗尽。
- 即使代码抛出异常,文件也能被正确关闭。
- 代码更简洁,逻辑更清晰。
对比手动管理:- # 推荐写法
- with open('example.txt', 'r') as f:
- data = f.read()
- # 文件已自动关闭
- # 不推荐的写法
- f = open('example.txt', 'r')
- try:
- data = f.read()
- finally:
- f.close()
复制代码
with语句等价于try...finally,但工作量更少。
with还支持同时打开多个文件:- with open('source.txt', 'r') as src, open('target.txt', 'w') as tgt:
- content = src.read()
- tgt.write(content)
复制代码
这样两个文件都会在退出with块时自动关闭。
注意事项:
- with只负责关闭文件,不处理文件不存在、权限不足等问题,需配合try...except。
- 离开with块后,文件对象已经关闭,再调用f.read()会抛出ValueError: I/O operation on closed file.。
- with语句也适用于网络连接、数据库连接、锁等需要资源管理的对象。
七、大文件处理技巧
处理数百MB甚至数GB的文件时,read()一次性加载全部内容会导致内存溢出。正确做法是流式读取。
1. 文本文件逐行处理:- with open('large_log.txt', 'r', encoding='utf-8') as f:
- for line in f:
- process_line(line) # process_line是你的处理函数
复制代码
文件对象本身是可迭代对象,每次迭代只读一行到内存。
2. 二进制或文本文件分块读取:- chunk_size = 1024 * 1024 # 每次读取1MB
- with open('large_file.bin', 'rb') as f:
- while True:
- chunk = f.read(chunk_size)
- if not chunk:
- break
- process_chunk(chunk)
复制代码
当read()返回空字符串或空字节串时,说明已到达文件末尾。
八、常见异常与处理
文件操作中常见异常包括文件不存在、权限不足、编码错误、其他I/O错误。建议用try...except统一捕获:- try:
- with open('non_existent.txt', 'r') as f:
- content = f.read()
- except FileNotFoundError:
- print("文件不存在,请检查路径。")
- except PermissionError:
- print("没有文件读写权限。")
- except UnicodeDecodeError:
- print("文件编码错误,请指定正确的encoding参数。")
- except IOError as e:
- print(f"发生I/O错误: {e}")
复制代码
注意捕获顺序:FileNotFoundError和PermissionError都是IOError的子类,但更具体的异常应放在前面,便于精确定位问题。
九、综合示例:复制文件
结合读写操作和异常处理,可以封装一个简单的文件复制函数:
- def copy_file(source_path, target_path):
- """复制文本文件"""
- try:
- with open(source_path, 'r', encoding='utf-8') as src:
- with open(target_path, 'w', encoding='utf-8') as tgt:
- for line in src:
- tgt.write(line)
- print(f"文件复制成功:{source_path} -> {target_path}")
- except Exception as e:
- print(f"复制失败: {e}")
- # 使用示例
- copy_file('source.txt', 'backup.txt')
复制代码
如果需要复制二进制文件,记得将模式改为'rb'和'wb',并采用分块读取避免大文件内存问题。
十、总结
Python文件操作的核心要点可以归纳为:
- 使用open()打开文件,用with语句管理上下文。
- 根据场景选择正确的模式:只读用'r',写入用'w',追加用'a',组合模式需谨慎。
- 小文件直接用read(),大文件用迭代或read(size)分块。
- 'w'模式会清空文件,'a'模式保留原有内容。
- 处理中文时设置encoding='utf-8'。
- 用try...except捕获FileNotFoundError、PermissionError、UnicodeDecodeError等异常。
掌握这些基础操作后,可以继续学习os和pathlib模块,实现更高级的路径管理和跨平台文件处理。 |