
引言:为什么需要文件操作?
在前面的课程中,我们处理的数据都是存储在内存中的。程序一旦结束,数据就消失了。
但如果我们需要:
- 保存程序运行的结果
- 读取外部配置文件
- 处理大量的数据文件
- 生成报告文件
这时候就需要用到文件操作。通过文件操作,我们可以将数据持久化保存到磁盘,也可以从磁盘读取数据。
文件操作是程序与外部世界交互的重要方式,掌握了文件操作,你的程序就能处理真实世界的数据了。
一、文件的打开和关闭
1. 打开文件
使用 open() 函数打开文件,返回一个文件对象。
python
# 基本语法
# file = open(文件名, 模式, 编码)
# 打开文件
file = open("example.txt", "r", encoding="utf-8")
# 使用完毕后关闭文件
file.close()
2. 文件打开模式
| 模式 | 说明 |
|---|---|
r |
只读模式(默认),文件不存在会报错 |
w |
写入模式,文件不存在会创建,存在会清空 |
a |
追加模式,文件不存在会创建 |
x |
创建模式,文件已存在会报错 |
b |
二进制模式,与其他模式组合使用 |
+ |
读写模式,与其他模式组合使用 |
python
# 只读模式
file = open("example.txt", "r")
# 写入模式(会清空原内容)
file = open("example.txt", "w")
# 追加模式
file = open("example.txt", "a")
# 二进制模式
file = open("image.png", "rb")
# 读写模式
file = open("example.txt", "r+")
3. 使用with语句(推荐)
使用 with 语句可以自动关闭文件,即使发生异常也能正确关闭。
python
# 推荐方式:使用with语句
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
# 文件在这里自动关闭
# 等价于
file = open("example.txt", "r", encoding="utf-8")
try:
content = file.read()
print(content)
finally:
file.close()
💡 提示 :始终使用
with语句打开文件,这样可以确保文件被正确关闭。
二、读写文本文件
1. 读取文件
读取整个文件
python
# 读取整个文件内容
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
逐行读取
python
# 逐行读取(推荐用于大文件)
with open("example.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.strip()) # strip()去除换行符
读取所有行到列表
python
# 读取所有行到列表
with open("example.txt", "r", encoding="utf-8") as file:
lines = file.readlines()
print(lines) # 每行包含换行符
# 去除换行符
lines = [line.strip() for line in lines]
print(lines)
读取指定字符数
python
# 读取指定数量的字符
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read(10) # 读取前10个字符
print(content)
2. 写入文件
写入字符串
python
# 写入文件(会清空原内容)
with open("output.txt", "w", encoding="utf-8") as file:
file.write("第一行内容\n")
file.write("第二行内容\n")
file.write("第三行内容\n")
写入多行
python
# 使用writelines写入多行
lines = ["第一行\n", "第二行\n", "第三行\n"]
with open("output.txt", "w", encoding="utf-8") as file:
file.writelines(lines)
追加内容
python
# 追加内容到文件末尾
with open("output.txt", "a", encoding="utf-8") as file:
file.write("追加的内容\n")
3. 文件位置操作
python
with open("example.txt", "r", encoding="utf-8") as file:
# 获取当前位置
print(f"当前位置:{file.tell()}")
# 读取一些内容
content = file.read(10)
print(f"读取后位置:{file.tell()}")
# 移动到指定位置
file.seek(0) # 移动到文件开头
print(f"移动后位置:{file.tell()}")
三、读写CSV文件
1. 什么是CSV文件
CSV(Comma-Separated Values)是一种简单的表格数据格式,用逗号分隔字段。
csv
姓名,年龄,城市
张三,20,北京
李四,25,上海
王五,22,广州
2. 使用csv模块读取CSV
python
import csv
# 读取CSV文件
with open("data.csv", "r", encoding="utf-8") as file:
reader = csv.reader(file)
# 读取表头
header = next(reader)
print(f"表头:{header}")
# 读取数据行
for row in reader:
print(row)
输出:
css
表头:['姓名', '年龄', '城市']
['张三', '20', '北京']
['李四', '25', '上海']
['王五', '22', '广州']
3. 使用DictReader读取CSV
python
import csv
# 使用DictReader,每行数据是字典
with open("data.csv", "r", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
print(f"姓名:{row['姓名']},年龄:{row['年龄']},城市:{row['城市']}")
输出:
姓名:张三,年龄:20,城市:北京
姓名:李四,年龄:25,城市:上海
姓名:王五,年龄:22,城市:广州
4. 写入CSV文件
python
import csv
# 写入CSV文件
data = [
["姓名", "年龄", "城市"],
["张三", 20, "北京"],
["李四", 25, "上海"],
["王五", 22, "广州"]
]
with open("output.csv", "w", encoding="utf-8", newline="") as file:
writer = csv.writer(file)
writer.writerows(data)
5. 使用DictWriter写入CSV
python
import csv
# 使用DictWriter写入
data = [
{"姓名": "张三", "年龄": 20, "城市": "北京"},
{"姓名": "李四", "年龄": 25, "城市": "上海"},
{"姓名": "王五", "年龄": 22, "城市": "广州"}
]
with open("output.csv", "w", encoding="utf-8", newline="") as file:
fieldnames = ["姓名", "年龄", "城市"]
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader() # 写入表头
writer.writerows(data) # 写入数据
四、读写JSON文件
1. 什么是JSON文件
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写。
json
{
"name": "小明",
"age": 20,
"scores": [85, 92, 95],
"address": {
"city": "北京",
"street": "长安街"
}
}
2. 使用json模块
读取JSON文件
python
import json
# 读取JSON文件
with open("data.json", "r", encoding="utf-8") as file:
data = json.load(file)
print(data)
print(f"姓名:{data['name']}")
print(f"年龄:{data['age']}")
写入JSON文件
python
import json
# 写入JSON文件
data = {
"name": "小明",
"age": 20,
"scores": [85, 92, 95],
"address": {
"city": "北京",
"street": "长安街"
}
}
with open("output.json", "w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=2)
💡 提示:
ensure_ascii=False:允许中文字符indent=2:格式化输出,缩进2个空格
3. JSON字符串操作
python
import json
# Python对象转JSON字符串
data = {"name": "小明", "age": 20}
json_str = json.dumps(data, ensure_ascii=False)
print(json_str) # 输出:{"name": "小明", "age": 20}
# JSON字符串转Python对象
json_str = '{"name": "小明", "age": 20}'
data = json.loads(json_str)
print(data) # 输出:{'name': '小明', 'age': 20}
4. JSON与Python类型对应
| Python | JSON |
|---|---|
| dict | object |
| list, tuple | array |
| str | string |
| int, float | number |
| True | true |
| False | false |
| None | null |
五、文件路径处理
1. 使用os.path模块
python
import os
# 当前工作目录
print(f"当前目录:{os.getcwd()}")
# 拼接路径
path = os.path.join("folder", "subfolder", "file.txt")
print(f"路径:{path}")
# 获取文件名
filename = os.path.basename("/path/to/file.txt")
print(f"文件名:{filename}") # 输出:file.txt
# 获取目录名
dirname = os.path.dirname("/path/to/file.txt")
print(f"目录名:{dirname}") # 输出:/path/to
# 分离文件名和扩展名
name, ext = os.path.splitext("file.txt")
print(f"文件名:{name},扩展名:{ext}") # 输出:file .txt
# 判断路径是否存在
if os.path.exists("/path/to/file.txt"):
print("文件存在")
else:
print("文件不存在")
# 判断是否是文件/目录
print(os.path.isfile("file.txt")) # 是否是文件
print(os.path.isdir("folder")) # 是否是目录
2. 使用pathlib模块(推荐)
pathlib 是Python 3.4+引入的面向对象的路径操作模块。
python
from pathlib import Path
# 创建路径对象
path = Path("folder/subfolder/file.txt")
print(path)
# 获取当前目录
cwd = Path.cwd()
print(f"当前目录:{cwd}")
# 获取主目录
home = Path.home()
print(f"主目录:{home}")
# 路径拼接
path = Path("folder") / "subfolder" / "file.txt"
print(path)
# 获取文件名和扩展名
path = Path("/path/to/file.txt")
print(f"文件名:{path.name}") # 输出:file.txt
print(f"文件 stem:{path.stem}") # 输出:file
print(f"扩展名:{path.suffix}") # 输出:.txt
print(f"父目录:{path.parent}") # 输出:/path/to
# 判断路径
print(path.exists()) # 是否存在
print(path.is_file()) # 是否是文件
print(path.is_dir()) # 是否是目录
# 创建目录
Path("new_folder").mkdir(exist_ok=True)
Path("parent/child").mkdir(parents=True, exist_ok=True)
# 遍历目录
folder = Path(".")
for file in folder.glob("*.txt"):
print(file)
# 递归遍历
for file in folder.rglob("*.txt"):
print(file)
3. 文件操作示例
python
from pathlib import Path
# 创建文件
file_path = Path("example.txt")
file_path.write_text("Hello, World!", encoding="utf-8")
# 读取文件
content = file_path.read_text(encoding="utf-8")
print(content)
# 追加内容
with file_path.open("a", encoding="utf-8") as f:
f.write("\n追加的内容")
# 删除文件
if file_path.exists():
file_path.unlink()
# 复制文件
import shutil
source = Path("source.txt")
target = Path("target.txt")
shutil.copy(source, target)
# 移动文件
source = Path("old_name.txt")
target = Path("new_name.txt")
source.rename(target)
六、实战练习
练习1:日志文件处理
python
from datetime import datetime
from pathlib import Path
def write_log(message, level="INFO"):
"""写入日志"""
log_file = Path("app.log")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] [{level}] {message}\n"
with log_file.open("a", encoding="utf-8") as f:
f.write(log_entry)
def read_logs(level=None):
"""读取日志"""
log_file = Path("app.log")
if not log_file.exists():
print("日志文件不存在")
return
logs = log_file.read_text(encoding="utf-8").splitlines()
if level:
logs = [log for log in logs if f"[{level}]" in log]
for log in logs:
print(log)
# 测试
write_log("程序启动", "INFO")
write_log("用户登录成功", "INFO")
write_log("数据库连接失败", "ERROR")
write_log("数据保存成功", "INFO")
print("\n所有日志:")
read_logs()
print("\n错误日志:")
read_logs("ERROR")
练习2:CSV数据分析
python
import csv
from pathlib import Path
def analyze_csv(file_path):
"""分析CSV文件"""
with open(file_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
data = list(reader)
# 统计信息
print(f"总记录数:{len(data)}")
# 计算平均分
scores = [int(row["score"]) for row in data]
avg_score = sum(scores) / len(scores)
print(f"平均分:{avg_score:.1f}")
# 找出最高分
max_score = max(scores)
top_students = [row for row in data if int(row["score"]) == max_score]
print(f"最高分:{max_score}")
for student in top_students:
print(f" {student['name']}")
# 按城市统计
city_count = {}
for row in data:
city = row["city"]
city_count[city] = city_count.get(city, 0) + 1
print("\n各城市人数:")
for city, count in sorted(city_count.items(), key=lambda x: x[1], reverse=True):
print(f" {city}:{count}人")
# 创建测试数据
test_data = [
["name", "age", "score", "city"],
["张三", "20", "85", "北京"],
["李四", "21", "92", "上海"],
["王五", "20", "78", "北京"],
["赵六", "22", "95", "广州"],
["钱七", "21", "88", "上海"]
]
with open("students.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerows(test_data)
# 分析
analyze_csv("students.csv")
练习3:JSON配置管理
python
import json
from pathlib import Path
class ConfigManager:
"""配置管理器"""
def __init__(self, config_file="config.json"):
self.config_file = Path(config_file)
self.config = {}
self.load()
def load(self):
"""加载配置"""
if self.config_file.exists():
with self.config_file.open("r", encoding="utf-8") as f:
self.config = json.load(f)
else:
self.config = {}
def save(self):
"""保存配置"""
with self.config_file.open("w", encoding="utf-8") as f:
json.dump(self.config, f, ensure_ascii=False, indent=2)
def get(self, key, default=None):
"""获取配置项"""
return self.config.get(key, default)
def set(self, key, value):
"""设置配置项"""
self.config[key] = value
self.save()
def delete(self, key):
"""删除配置项"""
if key in self.config:
del self.config[key]
self.save()
# 测试
config = ConfigManager()
# 设置配置
config.set("database", {
"host": "localhost",
"port": 3306,
"user": "root"
})
config.set("debug", True)
config.set("version", "1.0.0")
# 读取配置
print(f"数据库配置:{config.get('database')}")
print(f"调试模式:{config.get('debug')}")
print(f"版本:{config.get('version')}")
# 修改配置
config.set("debug", False)
print(f"修改后的调试模式:{config.get('debug')}")
# 删除配置
config.delete("version")
print(f"删除后的版本:{config.get('version', '未设置')}")
练习4:批量文件处理
python
from pathlib import Path
import shutil
def batch_rename(folder, prefix, suffix=".txt"):
"""批量重命名文件"""
folder = Path(folder)
if not folder.exists():
print(f"目录不存在:{folder}")
return
files = list(folder.glob(f"*{suffix}"))
print(f"找到 {len(files)} 个文件")
for i, file in enumerate(files, 1):
new_name = f"{prefix}_{i:03d}{suffix}"
new_path = file.parent / new_name
file.rename(new_path)
print(f"{file.name} -> {new_name}")
def batch_copy(source_folder, target_folder, pattern="*"):
"""批量复制文件"""
source = Path(source_folder)
target = Path(target_folder)
if not source.exists():
print(f"源目录不存在:{source}")
return
target.mkdir(parents=True, exist_ok=True)
files = list(source.glob(pattern))
print(f"找到 {len(files)} 个文件")
for file in files:
if file.is_file():
target_file = target / file.name
shutil.copy(file, target_file)
print(f"复制:{file.name}")
def find_large_files(folder, min_size_mb=10):
"""查找大文件"""
folder = Path(folder)
min_size = min_size_mb * 1024 * 1024
large_files = []
for file in folder.rglob("*"):
if file.is_file() and file.stat().st_size >= min_size:
size_mb = file.stat().st_size / (1024 * 1024)
large_files.append((file, size_mb))
large_files.sort(key=lambda x: x[1], reverse=True)
print(f"找到 {len(large_files)} 个大文件(>{min_size_mb}MB):")
for file, size in large_files:
print(f" {file}: {size:.2f}MB")
# 测试
# batch_rename("test_folder", "document")
# batch_copy("source", "backup", "*.txt")
# find_large_files(".", min_size_mb=1)
七、常见问题与排错
问题1:文件编码错误
原因 :文件编码与读取时指定的编码不一致 解决方法:指定正确的编码
python
# ❌ 错误:默认编码可能不匹配
with open("file.txt", "r") as f:
content = f.read() # UnicodeDecodeError
# ✅ 正确:指定编码
with open("file.txt", "r", encoding="utf-8") as f:
content = f.read()
问题2:文件不存在
原因 :文件路径错误或文件不存在 解决方法:先检查文件是否存在
python
from pathlib import Path
file_path = Path("data.txt")
# 方法1:使用exists()检查
if file_path.exists():
with file_path.open("r", encoding="utf-8") as f:
content = f.read()
else:
print(f"文件不存在:{file_path}")
# 方法2:使用try-except
try:
with file_path.open("r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
print(f"文件不存在:{file_path}")
问题3:权限错误
原因 :没有读写文件的权限 解决方法:检查文件权限或以管理员身份运行
python
# 检查文件是否可写
from pathlib import Path
import os
file_path = Path("test.txt")
# 检查是否可写
if os.access(file_path, os.W_OK):
print("文件可写")
else:
print("文件不可写")
# 检查是否可读
if os.access(file_path, os.R_OK):
print("文件可读")
else:
print("文件不可读")
八、本篇小结
今天,我们完成了以下学习内容:
-
文件的打开和关闭:
- 使用
open()打开文件 - 文件打开模式:r、w、a、x、b、+
- 使用
with语句自动关闭文件
- 使用
-
读写文本文件:
- 读取:
read()、readline()、readlines() - 写入:
write()、writelines() - 文件位置:
tell()、seek()
- 读取:
-
读写CSV文件:
- 使用
csv模块 reader()和writer()DictReader()和DictWriter()
- 使用
-
读写JSON文件:
- 使用
json模块 load()和dump()用于文件loads()和dumps()用于字符串
- 使用
-
文件路径处理:
- 使用
os.path模块 - 使用
pathlib模块(推荐) - 路径拼接、判断、遍历
- 使用
请记住这三个结论:
- 始终使用with语句:确保文件被正确关闭
- 指定编码 :读写文本文件时指定
encoding="utf-8" - pathlib更强大 :推荐使用
pathlib处理文件路径
下期预告
在第10期中,我们将学习Python的异常处理,包括:
- 异常的概念和类型
- try-except语句
- 自定义异常
- 异常处理的最佳实践
敬请期待!
✍坚持原创,求关注,点赞,收藏