Python文件操作与异常处理
1. 文件操作概述
作为Java开发者,您已经熟悉了使用File
类、FileInputStream
/FileOutputStream
或BufferedReader
/BufferedWriter
等进行文件操作。Python提供了更简洁的文件操作方式,使读写文件变得更加容易。
2. 基本文件操作
打开和关闭文件
在Python中,使用内置的open()
函数打开文件:
python
# 打开文件(默认为读取模式'r')
file = open('example.txt', 'r')
# 操作文件...
# 关闭文件
file.close()
常用的文件打开模式:
模式 | 描述 |
---|---|
'r' | 读取模式(默认) |
'w' | 写入模式(覆盖已有内容) |
'a' | 追加模式(在文件末尾添加内容) |
'b' | 二进制模式(与其他模式结合使用,如'rb'或'wb') |
't' | 文本模式(默认) |
'+' | 读写模式(与其他模式结合使用,如'r+'或'w+') |
使用上下文管理器(with语句)
推荐使用with
语句处理文件操作,它会自动关闭文件,即使发生异常:
python
# 使用with语句(推荐)
with open('example.txt', 'r', encoding='utf-8') as file:
content = file.read()
print(content)
# 文件会在with块结束时自动关闭
这类似于Java的try-with-resources语句:
java
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String content = reader.readLine();
System.out.println(content);
}
3. 读取文件
Python提供了多种读取文件的方法:
读取整个文件
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:
# 读取一行
line = file.readline()
print(line)
# 读取所有行到列表
lines = file.readlines()
print(lines)
迭代文件对象
python
with open('example.txt', 'r', encoding='utf-8') as file:
# 逐行迭代(内存效率高)
for line in file:
print(line.strip()) # strip()移除行尾的换行符
4. 写入文件
写入字符串
python
with open('output.txt', 'w', encoding='utf-8') as file:
# 写入单个字符串
file.write('Hello, Python!\n')
# 写入多行
file.write('这是第一行\n')
file.write('这是第二行\n')
写入多行
python
with open('output.txt', 'w', encoding='utf-8') as file:
# 写入多行
lines = ['第一行\n', '第二行\n', '第三行\n']
file.writelines(lines)
追加内容
python
with open('output.txt', 'a', encoding='utf-8') as file:
# 追加内容到文件末尾
file.write('这是追加的内容\n')
5. 文件和目录操作
Python的os
和shutil
模块提供了文件和目录操作的功能:
检查文件是否存在
python
import os
# 检查文件是否存在
if os.path.exists('example.txt'):
print('文件存在')
else:
print('文件不存在')
# 检查是文件还是目录
if os.path.isfile('example.txt'):
print('这是一个文件')
elif os.path.isdir('example_dir'):
print('这是一个目录')
文件路径操作
python
import os
# 获取当前工作目录
current_dir = os.getcwd()
print(f'当前目录: {current_dir}')
# 连接路径
file_path = os.path.join(current_dir, 'data', 'example.txt')
print(f'文件路径: {file_path}')
# 获取文件名和目录
dirname = os.path.dirname(file_path)
filename = os.path.basename(file_path)
print(f'目录: {dirname}')
print(f'文件名: {filename}')
# 分离文件名和扩展名
name, ext = os.path.splitext(filename)
print(f'文件名: {name}, 扩展名: {ext}')
创建和删除目录
python
import os
import shutil
# 创建单个目录
os.mkdir('new_directory')
# 递归创建目录
os.makedirs('parent/child/grandchild', exist_ok=True)
# 删除单个空目录
os.rmdir('empty_directory')
# 递归删除目录(包含内容)
shutil.rmtree('directory_to_delete')
列出目录内容
python
import os
# 列出目录中的文件和子目录
entries = os.listdir('.')
print(entries)
# 使用glob模块进行模式匹配
import glob
python_files = glob.glob('*.py')
print(f'Python文件: {python_files}')
# 递归遍历目录
for root, dirs, files in os.walk('.'):
print(f'当前目录: {root}')
print(f'子目录: {dirs}')
print(f'文件: {files}')
print('---')
6. 处理不同文件格式
CSV文件
python
import csv
# 读取CSV文件
with open('data.csv', 'r', encoding='utf-8', newline='') as file:
reader = csv.reader(file)
for row in reader:
print(row)
# 使用DictReader读取CSV(将每行转换为字典)
with open('data.csv', 'r', encoding='utf-8', newline='') as file:
reader = csv.DictReader(file)
for row in reader:
print(row) # 每行是一个字典,键是列名
# 写入CSV文件
with open('output.csv', 'w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
writer.writerow(['姓名', '年龄', '城市']) # 写入表头
writer.writerow(['张三', 30, '北京'])
writer.writerow(['李四', 25, '上海'])
# 使用DictWriter写入CSV
with open('output.csv', 'w', encoding='utf-8', newline='') as file:
fieldnames = ['姓名', '年龄', '城市']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader() # 写入表头
writer.writerow({'姓名': '张三', '年龄': 30, '城市': '北京'})
writer.writerow({'姓名': '李四', '年龄': 25, '城市': '上海'})
JSON文件
python
import json
# 读取JSON文件
with open('data.json', 'r', encoding='utf-8') as file:
data = json.load(file)
print(data)
# 写入JSON文件
data = {
'name': '张三',
'age': 30,
'city': '北京',
'skills': ['Python', 'Java', 'SQL']
}
with open('output.json', 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
XML文件
python
import xml.etree.ElementTree as ET
# 解析XML文件
tree = ET.parse('data.xml')
root = tree.getroot()
# 遍历XML元素
for child in root:
print(f'标签: {child.tag}, 属性: {child.attrib}')
for subchild in child:
print(f' 子标签: {subchild.tag}, 文本: {subchild.text}')
# 创建XML
root = ET.Element('root')
person = ET.SubElement(root, 'person', {'id': '1'})
name = ET.SubElement(person, 'name')
name.text = '张三'
age = ET.SubElement(person, 'age')
age.text = '30'
# 写入XML文件
tree = ET.ElementTree(root)
tree.write('output.xml', encoding='utf-8', xml_declaration=True)
Excel文件(使用pandas)
python
import pandas as pd
# 读取Excel文件
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
print(df.head())
# 写入Excel文件
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [30, 25, 35],
'城市': ['北京', '上海', '广州']
}
df = pd.DataFrame(data)
df.to_excel('output.xlsx', sheet_name='人员信息', index=False)
7. 异常处理
异常处理流程图
flowchart TD
A[开始] --> B{try 代码块}
B -->|发生异常| C{异常类型匹配?}
C -->|是| D[执行对应的except块]
C -->|否| E{有通用Exception?}
E -->|是| F[执行通用Exception处理]
E -->|否| G[异常向上传播]
B -->|无异常| H[执行else块]
D --> I[执行finally块]
F --> I
G --> I
H --> I
I --> J[结束]
style A fill:#98FB98,stroke:#333,stroke-width:2px
style J fill:#98FB98,stroke:#333,stroke-width:2px
style B fill:#FFB6C1,stroke:#333,stroke-width:2px
style C fill:#FFD700,stroke:#333,stroke-width:2px
style E fill:#FFD700,stroke:#333,stroke-width:2px
style I fill:#87CEFA,stroke:#333,stroke-width:2px
基本异常处理
Python使用try
、except
、else
和finally
进行异常处理:
python
try:
# 可能引发异常的代码
x = int(input("请输入一个数字: "))
result = 10 / x
print(f"结果是: {result}")
except ValueError:
# 处理ValueError异常
print("输入无效,请输入一个数字")
except ZeroDivisionError:
# 处理ZeroDivisionError异常
print("不能除以零")
except Exception as e:
# 处理其他所有异常
print(f"发生错误: {e}")
else:
# 如果try块中没有异常发生,则执行
print("计算成功完成")
finally:
# 无论是否发生异常,都会执行
print("异常处理结束")
对比Java的异常处理:
java
try {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个数字: ");
int x = scanner.nextInt();
double result = 10.0 / x;
System.out.println("结果是: " + result);
} catch (InputMismatchException e) {
System.out.println("输入无效,请输入一个数字");
} catch (ArithmeticException e) {
System.out.println("不能除以零");
} catch (Exception e) {
System.out.println("发生错误: " + e.getMessage());
} finally {
System.out.println("异常处理结束");
}
自定义异常
Python允许创建自定义异常类:
python
# 自定义异常类
class InvalidAgeError(Exception):
def __init__(self, age, message="年龄必须在0到120之间"):
self.age = age
self.message = message
super().__init__(self.message)
def __str__(self):
return f"{self.message}: {self.age}"
# 使用自定义异常
def validate_age(age):
if not 0 <= age <= 120:
raise InvalidAgeError(age)
return True
try:
age = int(input("请输入年龄: "))
validate_age(age)
print(f"年龄有效: {age}")
except InvalidAgeError as e:
print(e)
except ValueError:
print("请输入有效的数字")
使用上下文管理器处理资源
除了文件操作,上下文管理器也可用于其他资源管理:
python
# 自定义上下文管理器
class DatabaseConnection:
def __init__(self, connection_string):
self.connection_string = connection_string
self.connection = None
def __enter__(self):
print(f"连接到数据库: {self.connection_string}")
self.connection = "数据库连接对象" # 实际应用中,这里会创建真正的数据库连接
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
print("关闭数据库连接")
if exc_type is not None:
print(f"发生异常: {exc_type.__name__}: {exc_val}")
# 返回True表示异常已处理,False表示异常需要传播
return False
# 使用自定义上下文管理器
try:
with DatabaseConnection("mysql://localhost/mydb") as conn:
print("执行数据库操作")
# 模拟异常
# raise ValueError("数据库操作失败")
except Exception as e:
print(f"捕获到异常: {e}")
使用contextlib创建上下文管理器
Python的contextlib
模块提供了更简单的方式创建上下文管理器:
python
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
try:
f = open(filename, mode)
yield f
finally:
f.close()
# 使用装饰器创建的上下文管理器
with file_manager('example.txt', 'w') as f:
f.write('Hello, World!')
8. 实际应用:日志记录
结合文件操作和异常处理,实现简单的日志记录功能:
python
import os
import datetime
class Logger:
def __init__(self, log_file):
self.log_file = log_file
# 确保日志目录存在
log_dir = os.path.dirname(log_file)
if log_dir and not os.path.exists(log_dir):
os.makedirs(log_dir)
def log(self, message, level="INFO"):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] [{level}] {message}\n"
try:
with open(self.log_file, 'a', encoding='utf-8') as f:
f.write(log_entry)
except Exception as e:
print(f"写入日志失败: {e}")
def info(self, message):
self.log(message, "INFO")
def warning(self, message):
self.log(message, "WARNING")
def error(self, message):
self.log(message, "ERROR")
# 使用日志记录器
logger = Logger("logs/application.log")
try:
# 模拟应用程序操作
logger.info("应用程序启动")
# 模拟警告情况
user_input = "invalid_value"
if not user_input.isdigit():
logger.warning(f"收到无效输入: {user_input}")
# 模拟错误
raise ValueError("发生了一个错误")
except Exception as e:
logger.error(f"异常: {type(e).__name__}: {e}")
finally:
logger.info("应用程序关闭")
9. 练习:文件操作与异常处理
练习1:创建简单的文件备份工具
python
import os
import shutil
import datetime
def backup_file(file_path):
"""
创建文件的备份副本
"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件不存在: {file_path}")
if not os.path.isfile(file_path):
raise ValueError(f"不是一个文件: {file_path}")
# 获取文件名和扩展名
directory, filename = os.path.split(file_path)
name, ext = os.path.splitext(filename)
# 创建备份文件名(添加时间戳)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
backup_filename = f"{name}_backup_{timestamp}{ext}"
backup_path = os.path.join(directory, backup_filename)
# 复制文件
try:
shutil.copy2(file_path, backup_path)
print(f"已创建备份: {backup_path}")
return backup_path
except Exception as e:
print(f"备份失败: {e}")
return None
def main():
try:
file_path = input("请输入要备份的文件路径: ")
backup_path = backup_file(file_path)
if backup_path:
print("备份成功!")
# 显示原文件和备份文件的大小
original_size = os.path.getsize(file_path)
backup_size = os.path.getsize(backup_path)
print(f"原文件大小: {original_size} 字节")
print(f"备份文件大小: {backup_size} 字节")
except FileNotFoundError as e:
print(f"错误: {e}")
except ValueError as e:
print(f"错误: {e}")
except Exception as e:
print(f"发生未知错误: {e}")
if __name__ == "__main__":
main()
练习2:CSV数据处理
python
import csv
import os
def process_csv_data(input_file, output_file):
"""
处理CSV数据:读取输入文件,计算每个人的平均分,并写入输出文件
"""
try:
if not os.path.exists(input_file):
raise FileNotFoundError(f"输入文件不存在: {input_file}")
# 读取数据
students = []
with open(input_file, 'r', encoding='utf-8', newline='') as f:
reader = csv.DictReader(f)
for row in reader:
try:
# 转换分数为数字并计算平均分
scores = [int(row['语文']), int(row['数学']), int(row['英语'])]
average = sum(scores) / len(scores)
students.append({
'姓名': row['姓名'],
'语文': int(row['语文']),
'数学': int(row['数学']),
'英语': int(row['英语']),
'平均分': round(average, 2)
})
except (ValueError, KeyError) as e:
print(f"处理行 {row} 时出错: {e}")
# 按平均分排序
students.sort(key=lambda x: x['平均分'], reverse=True)
# 写入结果
with open(output_file, 'w', encoding='utf-8', newline='') as f:
fieldnames = ['姓名', '语文', '数学', '英语', '平均分', '排名']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for i, student in enumerate(students, 1):
student['排名'] = i
writer.writerow(student)
print(f"处理完成,结果已写入 {output_file}")
return True
except Exception as e:
print(f"处理CSV数据时出错: {e}")
return False
def main():
try:
input_file = input("请输入学生成绩CSV文件路径: ")
output_file = input("请输入结果文件路径: ")
success = process_csv_data(input_file, output_file)
if success:
print("数据处理成功!")
except Exception as e:
print(f"程序执行出错: {e}")
if __name__ == "__main__":
main()
10. 今日总结
- Python提供了简洁的文件操作API,使读写文件变得容易
with
语句(上下文管理器)是处理文件的推荐方式,它会自动关闭文件- Python的
os
和shutil
模块提供了丰富的文件和目录操作功能 - Python可以处理多种文件格式,如CSV、JSON、XML和Excel
- Python的异常处理使用
try
、except
、else
和finally
语句 - 可以创建自定义异常类和上下文管理器,增强代码的健壮性和可读性
11. 明日预告
明天我们将学习Python的高级特性,包括装饰器、生成器、迭代器、上下文管理器和函数式编程等高级概念,这些特性可以让您的Python代码更加简洁、高效和优雅。