文件操作
11.1 文件操作概述
11.1.1 为什么需要文件操作
程序运行时,数据存储在内存中,但程序结束后数据就会丢失。文件操作让程序能够:
持久化存储:将数据保存到磁盘,程序重启后仍然存在
数据交换:读取外部数据(数据集、配置文件),导出结果
处理大型数据:逐行读取,避免一次性加载到内存
11.1.2 文件操作的基本流程
# 1. 打开文件 f = open('filename.txt', 'r') # 2. 读取或写入 content = f.read() f.write('Hello') # 3. 关闭文件 f.close()11.2 打开文件:open()函数
11.2.1 基本语法
f = open('somefile.txt') # 只读模式(默认) f = open('somefile.txt', 'r') # 显式指定只读 f = open('somefile.txt', 'w') # 写入模式(覆盖) f = open('somefile.txt', 'a') # 追加模式 f = open('somefile.txt', 'x') # 独占创建模式(文件已存在时报错)11.2.2 文件模式
'r+'vs'w+':
'r+':可读可写,不截断文件,从头开始读写'w+':可读可写,但会清空文件(截断)
# 文本模式(默认)——自动解码/编码 f = open('text.txt', 'r') # 读取文本,默认 UTF-8 f = open('text.txt', 'w') # 写入文本 # 二进制模式——读写原始字节 f = open('image.png', 'rb') # 读取二进制文件 f = open('image.png', 'wb') # 写入二进制文件11.2.3 指定编码
# 指定编码读取 f = open('text.txt', 'r', encoding='utf-8') f = open('text.txt', 'r', encoding='gbk') # 错误处理 f = open('text.txt', 'r', encoding='ascii', errors='ignore')11.2.4 文件路径
# 相对路径 f = open('data.txt') # 当前目录 f = open('data/subdata.txt') # 子目录 # 绝对路径 f = open('/home/user/data.txt') # Linux/macOS f = open('C:\\Users\\user\\data.txt') # Windows(注意转义) f = open(r'C:\Users\user\data.txt') # 使用原始字符串11.3 读写文件
11.3.1 读取文本
f = open('data.txt', 'r', encoding='utf-8') # 读取全部内容 content = f.read() # 返回整个文件内容的字符串 # 读取指定字符数 content = f.read(100) # 读取前100个字符 # 读取一行 line = f.readline() # 返回一行(包含换行符) # 读取所有行 lines = f.readlines() # 返回列表,每行一个元素 f.close()示例:逐行读取
f = open('data.txt', 'r', encoding='utf-8') for line in f: # 文件对象本身就是可迭代的 line = line.strip() # 去除末尾换行符 print(line) f.close()11.3.2 写入文本
f = open('data.txt', 'w', encoding='utf-8') # 写入字符串 f.write('Hello, world!\n') # 写入多行(接收字符串列表) lines = ['Line 1\n', 'Line 2\n', 'Line 3\n'] f.writelines(lines) f.close()11.3.3 二进制文件
# 读取二进制文件 f = open('image.png', 'rb') data = f.read() # bytes 类型 f.close() # 写入二进制文件 f = open('output.png', 'wb') f.write(data) # 写入 bytes f.close()11.4 关闭文件的重要性
11.4.1 为什么要关闭
确保数据写入磁盘:Python 会缓冲写入数据,关闭时才会真正写入
释放系统资源:每个进程有文件打开数量限制
避免文件锁定问题:其他程序可能无法访问
11.4.2 缓冲问题演示
# 写入后立即关闭 f = open('test.txt', 'w') f.write('Hello') f.close() # 文件内容正常写入 # 写入后不关闭 f = open('test.txt', 'w') f.write('Hello') # 此时文件可能为空(数据在缓冲区)11.4.3flush()—— 强制刷新
f = open('test.txt', 'w') f.write('Hello') f.flush() # 强制将缓冲区数据写入磁盘 # 即使不关闭,数据也已写入11.5 上下文管理器:with语句(推荐)
11.5.1 基本用法
with语句会自动关闭文件,即使发生异常也不会遗漏。
with open('data.txt', 'r', encoding='utf-8') as f: content = f.read() print(content) # 文件在 with 块结束后自动关闭,无需显式调用 close()对比传统方式:
# 传统方式(容易忘记关闭) f = open('data.txt', 'r') try: content = f.read() finally: f.close() # 必须手动关闭 # with 方式(更简洁可靠) with open('data.txt', 'r') as f: content = f.read()11.5.2 处理多个文件
# 同时打开两个文件(复制文件) with open('source.txt', 'r') as src, open('dest.txt', 'w') as dst: for line in src: dst.write(line)11.5.3 上下文管理器的工作原理
上下文管理器必须实现两个方法:
__enter__():进入with块时调用,返回值赋给as后的变量__exit__(exc_type, exc_val, exc_tb):退出时调用,处理异常和清理
class FileContext: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() # 返回 False 表示不抑制异常 return False # 使用 with FileContext('test.txt', 'w') as f: f.write('Hello')11.6 文件定位:seek()和tell()
11.6.1tell()—— 获取当前位置
with open('data.txt', 'r') as f: print(f.tell()) # 0(文件开头) f.read(5) print(f.tell()) # 5(已读取5个字符)11.6.2seek()—— 移动文件指针
# seek(offset, whence) # whence: 0=文件开头(默认),1=当前位置,2=文件末尾 with open('data.txt', 'r') as f: # 跳到第10个字符位置 f.seek(10) # 从当前位置向后移动5个字符 f.seek(5, 1) # 跳到文件末尾前10个字符处 f.seek(-10, 2)示例:读取文件的最后一行
with open('data.txt', 'rb') as f: # 二进制模式 # 跳到文件末尾 f.seek(-100, 2) # 向前100字节 data = f.read().decode('utf-8') last_line = data.strip().split('\n')[-1] print(last_line)11.7 标准流
11.7.1 三个标准流
Python 提供了三个标准流对象(类似文件对象):
import sys # 从标准输入读取 text = sys.stdin.read() print('Read:', text) # 写入标准输出 sys.stdout.write('Hello, stdout!\n') # 写入标准错误 sys.stderr.write('Error message\n')11.7.2 重定向标准输出
import sys # 将输出重定向到文件 with open('output.txt', 'w') as f: sys.stdout = f print('This goes to file') # 不会显示在控制台 # 恢复标准输出 sys.stdout = sys.__stdout__ print('This goes to console')11.8 文件模式详解
11.8.1 文本模式 vs 二进制模式
文本模式(默认):
自动处理编码/解码(字符 ↔ 字节)
自动处理换行符转换(
\n↔ 平台换行符)读写
str类型
二进制模式:
不进行编码转换
不处理换行符
读写
bytes类型
# 文本模式(默认) with open('data.txt', 'r') as f: content = f.read() # str 类型 print(type(content)) # <class 'str'> # 二进制模式 with open('data.txt', 'rb') as f: content = f.read() # bytes 类型 print(type(content)) # <class 'bytes'>11.8.2 换行符处理
文本模式下的换行符转换:
读取时:将
\r\n和\r转换为\n写入时:将
\n转换为系统默认换行符(os.linesep)
# 禁用换行符转换 with open('data.txt', 'r', newline='') as f: line = f.readline() # 保留原始换行符 # 指定换行符 with open('data.txt', 'r', newline='\r\n') as f: line = f.readline()11.9 迭代文件内容
11.9.1 按字符读取
def process(char): print(char, end='') with open('data.txt', 'r') as f: char = f.read(1) while char: # 文件末尾返回空字符串 process(char) char = f.read(1) # 更简洁的方式 with open('data.txt', 'r') as f: for char in f.read(): # read() 将整个文件读入内存 process(char)11.9.2 按行读取(推荐)
# 方式1:直接迭代文件对象 with open('data.txt', 'r') as f: for line in f: line = line.rstrip('\n') # 去除换行符 process(line) # 方式2:readline 循环 with open('data.txt', 'r') as f: while True: line = f.readline() if not line: break process(line.rstrip('\n'))11.9.3 读取所有行到列表
# 适合小型文件 with open('data.txt', 'r') as f: lines = f.readlines() # 列表,每行一个元素 for line in lines: process(line)11.9.4 使用fileinput处理多个文件
import fileinput # 依次处理多个文件 for line in fileinput.input(['file1.txt', 'file2.txt']): print(f'{fileinput.filename()}: {line}', end='')11.9.5 懒加载 vs 一次性加载
11.10 文件操作示例
11.10.1 复制文件
def copy_file(src, dst): """逐块复制文件(适用于大文件)""" with open(src, 'rb') as src_f, open(dst, 'wb') as dst_f: while True: chunk = src_f.read(4096) # 每次读取4KB if not chunk: break dst_f.write(chunk) copy_file('source.bin', 'dest.bin')11.10.2 统计文件单词数
def word_count(filename): with open(filename, 'r', encoding='utf-8') as f: text = f.read() words = text.split() return len(words) print(word_count('data.txt'))11.10.3 文件搜索
def grep(filename, pattern): """在文件中搜索包含 pattern 的行""" with open(filename, 'r', encoding='utf-8') as f: for line in f: if pattern in line: print(line.rstrip('\n')) grep('data.txt', 'ERROR')11.10.4 CSV 文件处理
import csv # 写入 CSV data = [ ['Name', 'Age', 'Score'], ['Alice', 25, 92], ['Bob', 30, 85], ] with open('data.csv', 'w', newline='') as f: writer = csv.writer(f) writer.writerows(data) # 读取 CSV with open('data.csv', 'r') as f: reader = csv.reader(f) for row in reader: print(row) # 使用字典读写(有表头) with open('data.csv', 'w', newline='') as f: writer = csv.DictWriter(f, fieldnames=['Name', 'Age', 'Score']) writer.writeheader() writer.writerow({'Name': 'Alice', 'Age': 25, 'Score': 92}) with open('data.csv', 'r') as f: reader = csv.DictReader(f) for row in reader: print(row['Name'], row['Score'])