文件操作概述
文件操作是指通过程序对计算机磁盘上的文件进行读取、写入、追加等操作
在之前的笔记中,我们通过 print() 将数据输出到控制台,通过 input() 从键盘获取输入数据。但这种方式有一个明显的局限:程序结束后,所有数据就消失了 。
文件操作可以解决这个问题——将数据持久化 保存到硬盘文件中,下次运行程序时仍然能够读取回来。这是程序与外部存储交互的基本方式。
Python 中文件操作的完整流程:
1 2 3 4 5 6 7 8 """ 文件操作的标准三步流程: 1. 打开文件 — open() 函数,建立程序与文件的连接 2. 操作文件 — 读取(read)、写入(write)、追加(append)等 3. 关闭文件 — close() 方法,断开连接释放资源 """
文件的打开与关闭
使用 open() 函数打开一个文件,获取文件对象;操作完毕后用 close() 关闭文件
open() 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 """ open(file, mode, encoding) 常用参数说明: file: 文件路径(字符串),可以是相对路径或绝对路径 mode: 打开模式(字符串),决定对文件做什么操作 encoding: 编码格式(字符串),推荐始终指定为 "utf-8" 返回值: 文件对象,后续通过它来操作文件 """ f = open ("test.txt" , "r" , encoding="utf-8" ) f.close()
文件打开模式(mode)
模式
含义
文件不存在时
文件存在时
r
只读(默认)
报错 FileNotFoundError
从开头读取
w
只写
创建新文件
清空 原有内容后写入
a
追加写入
创建新文件
在末尾追加 内容
r+
读写
报错 FileNotFoundError
从开头读取,可写入
w+
读写
创建新文件
清空 原有内容
a+
读追加
创建新文件
在末尾追加,可读取
注意:w 模式会先清空文件原有内容 ,非常危险!如果只是想追加内容,请使用 a 模式。
文件的关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 """ 为什么要关闭文件? - 释放操作系统资源(同时打开的文件数量有限) - 确保数据真正写入磁盘(写入操作可能暂存在缓冲区) """ f = open ("test.txt" , "r" , encoding="utf-8" ) content = f.read() f.close() with open ("test.txt" , "r" , encoding="utf-8" ) as f: content = f.read()
文件的读取操作
从文件中获取内容,支持一次性读取全部、按行读取、按指定字节读取等多种方式
假设当前目录下有一个 poem.txt 文件,内容如下:
1 2 3 4 5 静夜思 床前明月光 疑是地上霜 举头望明月 低头思故乡
read() — 一次性读取全部内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 """ read(size) 方法: - 不指定 size 或 size=-1:读取文件的全部内容 - 指定 size:读取指定字节数的内容 返回一个字符串 """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: content = f.read() print (content)
1 2 3 4 5 6 with open ("poem.txt" , "r" , encoding="utf-8" ) as f: chunk = f.read(3 ) print (chunk) chunk2 = f.read(3 ) print (chunk2)
readline() — 逐行读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 """ readline() 方法: 每次读取一行内容(包括行尾的换行符 \n) 读取到文件末尾时返回空字符串 '' """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: line1 = f.readline() print (line1, end="" ) line2 = f.readline() print (line2, end="" ) with open ("poem.txt" , "r" , encoding="utf-8" ) as f: line_num = 1 while True : line = f.readline() if not line: break print (f"第{line_num} 行:{line} " , end="" ) line_num += 1
readlines() — 按行读取到列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 """ readlines() 方法: 一次性读取所有行,返回一个列表 列表中的每个元素就是文件的一行(包含 \n) """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: lines = f.readlines() print (lines) with open ("poem.txt" , "r" , encoding="utf-8" ) as f: lines = [line.strip() for line in f.readlines()] print (lines)
直接遍历文件对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 """ 文件对象本身是可迭代的,可以直接用 for 循环逐行读取 这是逐行读取最简洁、最推荐的方式 它不会一次性把所有行加载到内存,适合处理大文件 """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: for line in f: print (f"-> {line.strip()} " )
四种读取方式对比
方法
返回值
内存占用
适用场景
read()
字符串
高(一次性全部加载)
小文件
readline()
字符串(一行)
低(每次只读一行)
需手动控制逐行读取
readlines()
列表
高(一次性全部加载)
需要按索引访问行
for line in f
逐行字符串
低(逐行迭代)
大文件逐行处理(推荐)
文件的写入操作
将内容写入文件,支持覆盖写入(w 模式)和追加写入(a 模式)
write() — 写入字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 """ write(content) 方法: 将字符串 content 写入文件 返回值:写入的字符数 注意: - w 模式会清空文件后写入 - 不会自动添加换行符,需要手动加 \n """ with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.write("Hello Python\n" ) f.write("这是第二行\n" ) num = f.write("这是第三行" ) print (f"写入了 {num} 个字符" )
1 2 3 4 5 6 7 8 9 with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.write("第一次写入\n" ) with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.write("第二次写入\n" )
writelines() — 写入一个序列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 """ writelines(sequence) 方法: 将一个字符串序列(列表、元组等)写入文件 不会自动添加换行符! """ lines = ["第一行\n" , "第二行\n" , "第三行\n" ] with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.writelines(lines) bad_lines = ["Hello" , "World" , "Python" ] with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.writelines(bad_lines)
追加写入(a 模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 """ a 模式(append): - 如果文件不存在,创建新文件 - 如果文件存在,在末尾追加内容,不清空原有内容 """ with open ("log.txt" , "a" , encoding="utf-8" ) as f: f.write("第一条日志\n" ) with open ("log.txt" , "a" , encoding="utf-8" ) as f: f.write("第二条日志\n" ) with open ("log.txt" , "a" , encoding="utf-8" ) as f: f.write("第三条日志\n" )
文件指针与定位
文件指针标记了当前读取/写入的位置,可以通过 seek() 移动指针位置
理解文件指针
1 2 3 4 5 6 7 8 9 10 """ 文件指针就像光标,标记着"下一次读写操作的起始位置": - 打开文件时,指针在文件开头(位置 0) - 读取部分内容后,指针移动到已读内容的末尾 - 连续读取时,每次从上次结束的位置继续 """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: print (f"初始位置:{f.tell()} " ) line = f.readline() print (f"读取一行后位置:{f.tell()} " )
seek() 与 tell()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 """ f.tell():返回当前文件指针的位置(字节偏移量) f.seek(offset, whence):移动文件指针到指定位置 参数说明: - offset:偏移量(字节数),可为负数 - whence:参照位置 0 — 文件开头(默认) 1 — 当前位置 2 — 文件末尾 """ with open ("poem.txt" , "r" , encoding="utf-8" ) as f: print (f.read(2 )) pos = f.tell() print (f"当前指针位置:{pos} " ) f.seek(0 ) print (f"回到开头后读取:{f.read(2 )} " )
文件与数据容器的结合
将数据容器与文件操作结合,实现数据的持久化存储和读取
列表与文件的互转
1 2 3 4 5 6 7 8 9 10 11 12 13 fruits = ["苹果" , "香蕉" , "橘子" , "葡萄" ] with open ("fruits.txt" , "w" , encoding="utf-8" ) as f: for fruit in fruits: f.write(fruit + "\n" ) loaded_fruits = [] with open ("fruits.txt" , "r" , encoding="utf-8" ) as f: for line in f: loaded_fruits.append(line.strip()) print (loaded_fruits)
字典与文件的互转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 config = { "host" : "127.0.0.1" , "port" : "8080" , "debug" : "True" } with open ("config.ini" , "w" , encoding="utf-8" ) as f: for key, value in config.items(): f.write(f"{key} ={value} \n" ) loaded_config = {} with open ("config.ini" , "r" , encoding="utf-8" ) as f: for line in f: line = line.strip() if "=" in line: key, value = line.split("=" , 1 ) loaded_config[key] = value print (loaded_config)
文件操作的异常处理
文件操作可能因各种原因失败(文件不存在、权限不足等),需要使用异常处理来保证程序健壮性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 """ 常见的文件操作异常: - FileNotFoundError:文件不存在(r 模式下) - PermissionError:没有读写权限 - IOError / OSError:其他 I/O 错误 """ filename = "maybe_not_exist.txt" try : with open (filename, "r" , encoding="utf-8" ) as f: content = f.read() print (content) except FileNotFoundError: print (f"错误:文件 '{filename} ' 不存在,请检查路径" ) except PermissionError: print (f"错误:没有权限读取文件 '{filename} '" ) except Exception as e: print (f"发生未知错误:{e} " )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def safe_write (filename, content ): """安全地将内容写入文件""" try : with open (filename, "w" , encoding="utf-8" ) as f: f.write(content) print (f"成功写入文件:{filename} " ) return True except PermissionError: print (f"错误:没有权限写入文件 '{filename} '" ) except Exception as e: print (f"写入失败:{e} " ) return False safe_write("test.txt" , "Hello World" )
综合练习
将所学知识融会贯通,通过实际案例巩固文件操作的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 """ 实现一个日志记录函数,将带时间戳的日志信息追加写入文件 """ import datetimedef write_log (filename, message ): """将日志信息追加写入指定文件""" timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S" ) log_line = f"[{timestamp} ] {message} \n" with open (filename, "a" , encoding="utf-8" ) as f: f.write(log_line) def read_log (filename ): """读取并打印日志文件内容""" try : with open (filename, "r" , encoding="utf-8" ) as f: print (f"===== {filename} =====" ) for line in f: print (line, end="" ) print ("=" * 40 ) except FileNotFoundError: print (f"日志文件 '{filename} ' 还不存在" ) write_log("app.log" , "程序启动" ) write_log("app.log" , "用户登录:admin" ) write_log("app.log" , "执行数据备份" ) write_log("app.log" , "程序关闭" ) read_log("app.log" )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 """ 从文件读取学生成绩,计算统计信息,并将结果写回文件 """ sample_data = """小明 85 92 78 小红 95 88 91 小刚 72 68 75 小丽 88 90 86 小华 60 55 58 """ with open ("scores.txt" , "w" , encoding="utf-8" ) as f: f.write(sample_data) students = [] with open ("scores.txt" , "r" , encoding="utf-8" ) as f: for line in f: parts = line.strip().split() if len (parts) >= 4 : name = parts[0 ] scores = [int (s) for s in parts[1 :]] avg = sum (scores) / len (scores) total = sum (scores) students.append({ "name" : name, "scores" : scores, "total" : total, "avg" : avg }) students.sort(key=lambda s: s["avg" ], reverse=True ) with open ("scores_report.txt" , "w" , encoding="utf-8" ) as f: f.write("===== 学生成绩报表 =====\n" ) f.write(f"{'排名' :<4 } {'姓名' :<6 } {'总分' :<6 } {'平均分' :<6 } {'评级' :<6 } \n" ) f.write("-" * 35 + "\n" ) for rank, stu in enumerate (students, 1 ): if stu["avg" ] >= 90 : grade = "优秀" elif stu["avg" ] >= 80 : grade = "良好" elif stu["avg" ] >= 70 : grade = "中等" elif stu["avg" ] >= 60 : grade = "及格" else : grade = "不及格" line = f"{rank:<4 } {stu['name' ]:<6 } {stu['total' ]:<6 } {stu['avg' ]:<6.1 f} {grade:<6 } \n" f.write(line) all_scores = [s for stu in students for s in stu["scores" ]] f.write("-" * 35 + "\n" ) f.write(f"总人数:{len (students)} \n" ) f.write(f"最高平均分:{students[0 ]['avg' ]:.1 f} ({students[0 ]['name' ]} )\n" ) f.write(f"最低平均分:{students[-1 ]['avg' ]:.1 f} ({students[-1 ]['name' ]} )\n" ) f.write(f"全班总平均分:{sum (all_scores) / len (all_scores):.1 f} \n" ) f.write(f"及格人数:{sum (1 for s in students if s['avg' ] >= 60 )} \n" ) with open ("scores_report.txt" , "r" , encoding="utf-8" ) as f: print (f.read())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 """ 复制一个文件到另一个位置 使用分块读取的方式,避免一次性加载整个文件到内存 """ def copy_file (source, target, chunk_size=1024 ): """ 复制文件 source: 源文件路径 target: 目标文件路径 chunk_size: 每次读写的字节数(默认1KB) """ try : with open (source, "rb" ) as src, open (target, "wb" ) as dst: total_bytes = 0 while True : chunk = src.read(chunk_size) if not chunk: break dst.write(chunk) total_bytes += len (chunk) print (f"复制完成!共 {total_bytes} 字节" ) return True except FileNotFoundError: print (f"源文件 '{source} ' 不存在" ) except Exception as e: print (f"复制失败:{e} " ) return False
文件操作是程序与外部世界交换数据的基础。掌握 open() 的三种模式(r/w/a)、四种读取方式、with 语句的自动关闭,以及异常处理,就能安全高效地完成各种文件读写任务。记住:操作文件时永远要考虑"文件不存在怎么办"和"操作完记得关闭"。
Python Study Note 系列文章
Python Study Note Python 函数使用(四) Python 判断语句(二) Python 基础语法(一) Python 异常处理(七) Python 循环语句(三) Python 数据容器(五) Python 文件基础操作(六) Python 模块与包(八) Python 类型注解(十) Python 面向对象(九)重点 Python 高阶技巧(十一)