04 - 文件操作与异常处理¶
学习时间: 45-60分钟 重要性: ⭐⭐⭐⭐⭐ 实际开发必备
🎯 学习目标¶
- 掌握文件的读写操作
- 理解上下文管理器(with语句)
- 学会正确处理异常
- 了解路径操作(pathlib)
📁 文件读写¶
基本读写操作¶
Python
# 读取文件
# 方法1: 一次性读取全部(小文件)
with open("example.txt", "r", encoding="utf-8") as f: # with语句自动管理文件关闭,即使出错也保证资源释放
content = f.read()
print(content)
# 方法2: 逐行读取(大文件)
with open("example.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip()) # strip()去除换行符
# 方法3: 读取所有行到列表
with open("example.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
print(lines) # ['line1\n', 'line2\n', 'line3\n']
# 写入文件
# 覆盖写入
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
f.write("第二行\n")
# 追加写入
with open("output.txt", "a", encoding="utf-8") as f:
f.write("这是新的一行\n")
# 写入多行
lines = ["第一行\n", "第二行\n", "第三行\n"]
with open("output.txt", "w", encoding="utf-8") as f:
f.writelines(lines)
文件模式¶
Python
# 模式列表
"""
'r' - 只读(默认)
'w' - 写入(会覆盖已存在的文件)
'a' - 追加(在文件末尾写入)
'x' - 独占创建(文件存在则失败)
'b' - 二进制模式
't' - 文本模式(默认)
'+' - 更新模式(读写)
"""
# 组合模式
# 'rb' - 读取二进制(图片、视频等)
with open("image.jpg", "rb") as f:
image_data = f.read()
# 'wb' - 写入二进制
with open("output.jpg", "wb") as f:
f.write(image_data)
# 'r+' - 读写(文件必须存在)
with open("data.txt", "r+", encoding="utf-8") as f:
content = f.read()
f.write("追加的内容")
with语句的重要性¶
Python
# ❌ 不好:可能忘记关闭文件
f = open("file.txt", "r")
content = f.read()
# 如果这里出现异常,文件不会关闭
f.close()
# ✅ 好:使用with语句,自动关闭
with open("file.txt", "r") as f:
content = f.read()
# 无论是否异常,文件都会自动关闭
# 等价于
f = open("file.txt", "r")
try:
content = f.read()
finally:
f.close()
📂 pathlib - 现代化的路径操作¶
基本使用¶
Python
from pathlib import Path
# 创建路径对象
path = Path("data/example.txt")
# 路径拼接(推荐)
path = Path("data") / "example.txt"
# 或
path = Path("data").joinpath("example.txt")
# 路径信息
print(path.name) # "example.txt" 文件名
print(path.stem) # "example" 不含扩展名
print(path.suffix) # ".txt" 扩展名
print(path.parent) # "data" 父目录
print(path.exists()) # True/False 是否存在
print(path.is_file()) # True/False 是否是文件
print(path.is_dir()) # True/False 是否是目录
# 读写文件
content = path.read_text(encoding="utf-8") # 读取文本
path.write_text("Hello, World!", encoding="utf-8") # 写入文本
data = path.read_bytes() # 读取二进制
path.write_bytes(b"binary data") # 写入二进制
目录操作¶
Python
from pathlib import Path
# 创建目录
Path("data/subdir").mkdir(parents=True, exist_ok=True)
# parents=True: 创建父目录
# exist_ok=True: 目录存在不报错
# 遍历目录
path = Path("data")
for item in path.iterdir():
if item.is_file():
print(f"文件: {item.name}")
elif item.is_dir():
print(f"目录: {item.name}")
# 递归遍历
for py_file in Path(".").rglob("*.py"):
print(py_file)
# 只查找当前目录
for txt_file in Path(".").glob("*.txt"):
print(txt_file)
实用示例¶
Python
from pathlib import Path
# 重命名文件
Path("old.txt").rename("new.txt")
# 移动文件
Path("data.txt").rename("backup/data.txt")
# 删除文件
Path("temp.txt").unlink()
# 删除目录(必须是空目录)
Path("temp_dir").rmdir()
# 获取绝对路径
path = Path("example.txt")
print(path.absolute())
# 获取相对路径
path = Path("data/example.txt")
print(path.relative_to("project"))
# 检查文件扩展名
if path.suffix in [".jpg", ".png", ".gif"]:
print("这是图片文件")
⚠️ 异常处理¶
基本语法¶
Python
# 基本结构
try:
# 可能出错的代码
result = 10 / 0
except ZeroDivisionError as e:
# 捕获特定异常
print(f"错误: {e}")
except ValueError as e:
# 捕获另一种异常
print(f"值错误: {e}")
else:
# 没有异常时执行
print(f"结果: {result}")
finally:
# 无论是否异常都执行
print("清理工作")
# 捕获多个异常
try:
number = int(input("输入数字: "))
result = 100 / number
except (ValueError, ZeroDivisionError) as e:
print(f"错误: {e}")
常见异常类型¶
Python
# 1. FileNotFoundError
try:
with open("不存在的文件.txt", "r") as f:
content = f.read()
except FileNotFoundError:
print("文件不存在")
# 2. ValueError
try:
number = int("abc")
except ValueError:
print("无法转换为整数")
# 3. TypeError
try:
result = "10" + 5 # 字符串+数字
except TypeError:
print("类型错误")
# 4. KeyError (字典)
person = {"name": "张三"}
try:
print(person["age"]) # 键不存在
except KeyError:
print("键不存在")
# 更好的方式:使用get()
age = person.get("age", "未知") # 不会报错
# 5. IndexError (列表)
numbers = [1, 2, 3]
try:
print(numbers[10]) # 索引超出范围
except IndexError:
print("索引超出范围")
# 6. AttributeError
class Person:
pass
person = Person()
try:
print(person.name) # 属性不存在
except AttributeError:
print("属性不存在")
最佳实践¶
Python
# 1. 明确指定异常类型(不要用裸except)
# ❌ 不好
try:
result = 10 / 0
except: # 捕获所有异常,包括SystemExit等
pass
# ✅ 好
try:
result = 10 / 0
except ZeroDivisionError:
pass
# 2. 提供有用的错误信息
try:
with open(filepath, "r") as f:
data = json.load(f) # json.load从文件读取JSON数据;json.dump将对象写入文件为JSON
except FileNotFoundError:
logging.error(f"配置文件不存在: {filepath}")
raise # 重新抛出异常
except json.JSONDecodeError as e:
logging.error(f"配置文件格式错误: {filepath}")
raise ValueError(f"Invalid JSON in {filepath}") from e
# 3. 使用finally进行清理
def process_file(filepath):
f = None
try:
f = open(filepath, "r")
# 处理文件
return f.read()
except IOError as e:
print(f"文件错误: {e}")
finally:
if f:
f.close() # 确保文件关闭
💡 实用模式¶
安全的文件读取¶
Python
def read_file_safely(filepath):
"""
安全地读取文件,处理各种异常
"""
try:
return Path(filepath).read_text(encoding="utf-8")
except FileNotFoundError:
print(f"错误: 文件不存在 - {filepath}")
except PermissionError:
print(f"错误: 没有权限读取文件 - {filepath}")
except UnicodeDecodeError:
print(f"错误: 文件编码不是UTF-8 - {filepath}")
except Exception as e:
print(f"未知错误: {e}")
return None
# 使用
content = read_file_safely("example.txt")
if content:
print(content)
JSON文件处理¶
Python
import json
from pathlib import Path
# 读取JSON
def load_json(filepath):
try:
content = Path(filepath).read_text(encoding="utf-8")
return json.loads(content)
except FileNotFoundError:
print(f"文件不存在: {filepath}")
except json.JSONDecodeError as e:
print(f"JSON格式错误: {e}")
return None
# 写入JSON(格式化)
def save_json(data, filepath):
try:
content = json.dumps(data, ensure_ascii=False, indent=2)
Path(filepath).write_text(content, encoding="utf-8")
return True
except Exception as e:
print(f"保存失败: {e}")
return False
# 使用
data = {
"name": "张三",
"age": 25,
"scores": [85, 90, 78]
}
save_json(data, "data.json")
loaded_data = load_json("data.json")
CSV文件处理(基础)¶
Python
import csv
from pathlib import Path
# 读取CSV
def read_csv(filepath):
data = []
try:
with open(filepath, "r", encoding="utf-8") as f:
reader = csv.DictReader(f) # 使用字典读取
for row in reader:
data.append(row)
return data
except Exception as e:
print(f"读取CSV失败: {e}")
return None
# 写入CSV
def write_csv(data, filepath):
try:
with open(filepath, "w", encoding="utf-8", newline="") as f:
if data:
writer = csv.DictWriter(f, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
return True
except Exception as e:
print(f"写入CSV失败: {e}")
return False
# 使用
data = [
{"name": "张三", "age": 25, "city": "北京"},
{"name": "李四", "age": 30, "city": "上海"}
]
write_csv(data, "output.csv")
loaded_data = read_csv("output.csv")
配置文件处理¶
Python
import json
from pathlib import Path
class Config:
"""简单的配置管理器"""
def __init__(self, filepath="config.json"):
self.filepath = Path(filepath)
self.config = {}
self.load()
def load(self):
"""加载配置"""
if self.filepath.exists():
try:
content = self.filepath.read_text(encoding="utf-8")
self.config = json.loads(content)
except Exception as e:
print(f"加载配置失败: {e}")
self.config = {}
else:
# 创建默认配置
self.config = self.get_default_config()
self.save()
def save(self):
"""保存配置"""
try:
content = json.dumps(self.config, ensure_ascii=False, indent=2)
self.filepath.write_text(content, encoding="utf-8")
return True
except Exception as e:
print(f"保存配置失败: {e}")
return False
def get(self, key, default=None):
"""获取配置项"""
return self.config.get(key, default)
def set(self, key, value):
"""设置配置项"""
self.config[key] = value
return self.save()
@staticmethod
def get_default_config():
return {
"debug": False,
"max_retries": 3,
"timeout": 30
}
# 使用
config = Config()
debug_mode = config.get("debug", False)
config.set("max_retries", 5)
📝 练习¶
练习1: 文件词频统计¶
Python
def word_frequency(filepath):
"""
读取文本文件,统计每个词出现的频率
返回字典:{单词: 频率}
"""
# TODO: 实现这个函数
# 提示:
# 1. 读取文件
# 2. 分词(split())
# 3. 统计频率(使用字典)
pass
# 测试
result = word_frequency("example.txt")
print(result)
练习2: 批量处理文件¶
Python
from pathlib import Path
def batch_rename(directory, pattern, replacement):
"""
批量重命名目录中的文件
将文件名中的pattern替换为replacement
例如: batch_rename("data/", "old", "new")
将 file_old_1.txt -> file_new_1.txt
"""
# TODO: 实现这个函数
pass
练习3: 异常安全的配置加载¶
Python
def load_config_with_defaults(filepath, defaults):
"""
加载配置文件,如果加载失败或缺少某些键,
使用默认值填充
filepath: 配置文件路径(JSON格式)
defaults: 默认配置字典
"""
# TODO: 实现这个函数
pass
🎯 自我检查¶
完成这个主题后,你应该:
- 能使用with语句读写文件
- 理解pathlib的基本使用
- 能正确处理常见异常
- 知道如何处理JSON和CSV文件
- 能写出异常安全的代码
- 不查资料完成上面的练习
📚 延伸阅读¶
下一步: 05 - 装饰器与生成器