文件操作概述

文件操作是指通过程序对计算机磁盘上的文件进行读取、写入、追加等操作

在之前的笔记中,我们通过 print() 将数据输出到控制台,通过 input() 从键盘获取输入数据。但这种方式有一个明显的局限:程序结束后,所有数据就消失了

文件操作可以解决这个问题——将数据持久化保存到硬盘文件中,下次运行程序时仍然能够读取回来。这是程序与外部存储交互的基本方式。

Python 中文件操作的完整流程:

1
2
3
4
5
6
7
8
"""
文件操作的标准三步流程:
1. 打开文件 — open() 函数,建立程序与文件的连接
2. 操作文件 — 读取(read)、写入(write)、追加(append)等
3. 关闭文件 — close() 方法,断开连接释放资源
"""

# 示意图:程序 → open() → 文件对象 → read/write → 磁盘文件 → close()

文件的打开与关闭

使用 open() 函数打开一个文件,获取文件对象;操作完毕后用 close() 关闭文件

  1. 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 进行操作...
f.close() # 操作完毕后关闭
  1. 文件打开模式(mode)
模式 含义 文件不存在时 文件存在时
r 只读(默认) 报错 FileNotFoundError 从开头读取
w 只写 创建新文件 清空原有内容后写入
a 追加写入 创建新文件 在末尾追加内容
r+ 读写 报错 FileNotFoundError 从开头读取,可写入
w+ 读写 创建新文件 清空原有内容
a+ 读追加 创建新文件 在末尾追加,可读取

注意:w 模式会先清空文件原有内容,非常危险!如果只是想追加内容,请使用 a 模式。

  1. 文件的关闭
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 语句(推荐!自动关闭)
with open("test.txt", "r", encoding="utf-8") as f:
content = f.read()
# 缩进块结束后,文件自动关闭,无需手动调用 close()

文件的读取操作

从文件中获取内容,支持一次性读取全部、按行读取、按指定字节读取等多种方式

假设当前目录下有一个 poem.txt 文件,内容如下:

1
2
3
4
5
静夜思
床前明月光
疑是地上霜
举头望明月
低头思故乡
  1. 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
# read(size) 指定字节数读取
with open("poem.txt", "r", encoding="utf-8") as f:
chunk = f.read(3) # 读取前3个字符
print(chunk) # 输出 静夜思
chunk2 = f.read(3) # 继续读取接下来的3个字符
print(chunk2) # 输出 \n床前
  1. 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: # 读到末尾,line 为空字符串
break
print(f"第{line_num}行:{line}", end="")
line_num += 1

# 输出:
# 第1行:静夜思
# 第2行:床前明月光
# 第3行:疑是地上霜
# 第4行:举头望明月
# 第5行:低头思故乡
  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)

# 输出:['静夜思\n', '床前明月光\n', '疑是地上霜\n', '举头望明月\n', '低头思故乡\n']

# 配合 strip() 去掉换行符
with open("poem.txt", "r", encoding="utf-8") as f:
lines = [line.strip() for line in f.readlines()]
print(lines)

# 输出:['静夜思', '床前明月光', '疑是地上霜', '举头望明月', '低头思故乡']
  1. 直接遍历文件对象
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()}")

# 输出:
# -> 静夜思
# -> 床前明月光
# -> 疑是地上霜
# -> 举头望明月
# -> 低头思故乡
  1. 四种读取方式对比
方法 返回值 内存占用 适用场景
read() 字符串 高(一次性全部加载) 小文件
readline() 字符串(一行) 低(每次只读一行) 需手动控制逐行读取
readlines() 列表 高(一次性全部加载) 需要按索引访问行
for line in f 逐行字符串 低(逐行迭代) 大文件逐行处理(推荐)

文件的写入操作

将内容写入文件,支持覆盖写入(w 模式)和追加写入(a 模式)

  1. 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
"""
# w 模式:覆盖写入(原有内容会被清空!)
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello Python\n")
f.write("这是第二行\n")
num = f.write("这是第三行")
print(f"写入了 {num} 个字符") # 输出 写入了 5 个字符

# 执行后,output.txt 内容为:
# Hello Python
# 这是第二行
# 这是第三行
1
2
3
4
5
6
7
8
9
# 多次以 w 模式打开同一文件,后一次会覆盖前一次
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")

# output.txt 最终内容只有:
# 第二次写入
  1. 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)

# 执行后,output.txt 内容为:
# 第一行
# 第二行
# 第三行

# 如果忘记加 \n,内容会连在一起
bad_lines = ["Hello", "World", "Python"]
with open("output.txt", "w", encoding="utf-8") as f:
f.writelines(bad_lines)

# output.txt 内容:HelloWorldPython(全挤在一起!)
  1. 追加写入(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")

# log.txt 最终内容(三次运行累积下来的):
# 第一条日志
# 第二条日志
# 第三条日志

文件指针与定位

文件指针标记了当前读取/写入的位置,可以通过 seek() 移动指针位置

  1. 理解文件指针
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()}") # 输出 初始位置:0
line = f.readline()
print(f"读取一行后位置:{f.tell()}") # 输出当前位置(取决于该行字节数)
  1. 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}")

# seek(0) 回到文件开头
f.seek(0)
print(f"回到开头后读取:{f.read(2)}") # 输出 静夜(又从头读了)

文件与数据容器的结合

将数据容器与文件操作结合,实现数据的持久化存储和读取

  1. 列表与文件的互转
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. 字典与文件的互转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 将字典保存为简单配置文件(key=value 格式)
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)
# 输出 {'host': '127.0.0.1', 'port': '8080', 'debug': 'True'}

文件操作的异常处理

文件操作可能因各种原因失败(文件不存在、权限不足等),需要使用异常处理来保证程序健壮性

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")
# 输出 成功写入文件:test.txt

综合练习

将所学知识融会贯通,通过实际案例巩固文件操作的使用

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 datetime

def 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")

# 输出:
# ===== app.log =====
# [2026-05-31 10:30:00] 程序启动
# [2026-05-31 10:30:01] 用户登录:admin
# [2026-05-31 10:30:02] 执行数据备份
# [2026-05-31 10:30:03] 程序关闭
# ========================================
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
# 学生成绩文件管理器
"""
从文件读取学生成绩,计算统计信息,并将结果写回文件
"""
# 1. 创建示例成绩文件
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)

# 2. 读取成绩并计算统计信息
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
})

# 3. 排序并生成报表
students.sort(key=lambda s: s["avg"], reverse=True)

# 4. 写入报表文件
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.1f} {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']:.1f} ({students[0]['name']})\n")
f.write(f"最低平均分:{students[-1]['avg']:.1f} ({students[-1]['name']})\n")
f.write(f"全班总平均分:{sum(all_scores) / len(all_scores):.1f}\n")
f.write(f"及格人数:{sum(1 for s in students if s['avg'] >= 60)}\n")

# 5. 打印生成的报表
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

# 使用示例
# copy_file("source.jpg", "backup.jpg")
# 注意:使用 "rb" / "wb" 二进制模式,适用于任何类型的文件

文件操作是程序与外部世界交换数据的基础。掌握 open() 的三种模式(r/w/a)、四种读取方式、with 语句的自动关闭,以及异常处理,就能安全高效地完成各种文件读写任务。记住:操作文件时永远要考虑"文件不存在怎么办"和"操作完记得关闭"。

Python Study Note 系列文章

  1. Python Study Note
  2. Python 函数使用(四)
  3. Python 判断语句(二)
  4. Python 基础语法(一)
  5. Python 异常处理(七)
  6. Python 循环语句(三)
  7. Python 数据容器(五)
  8. Python 文件基础操作(六)
  9. Python 模块与包(八)
  10. Python 类型注解(十)
  11. Python 面向对象(九)重点
  12. Python 高阶技巧(十一)