6、Python文件操作与异常处理

Python文件操作与异常处理

1. 文件操作概述

作为Java开发者,您已经熟悉了使用File类、FileInputStream/FileOutputStreamBufferedReader/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的osshutil模块提供了文件和目录操作的功能:

检查文件是否存在

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使用tryexceptelsefinally进行异常处理:

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的osshutil模块提供了丰富的文件和目录操作功能
  • Python可以处理多种文件格式,如CSV、JSON、XML和Excel
  • Python的异常处理使用tryexceptelsefinally语句
  • 可以创建自定义异常类和上下文管理器,增强代码的健壮性和可读性

11. 明日预告

明天我们将学习Python的高级特性,包括装饰器、生成器、迭代器、上下文管理器和函数式编程等高级概念,这些特性可以让您的Python代码更加简洁、高效和优雅。

相关推荐
dylan_QAQ几秒前
【附录】为什么说 Spring 中 BeanFactory的是延迟加载 和轻量级的?有什么证据?
后端·spring
回家路上绕了弯26 分钟前
深度理解 volatile 与 synchronized:并发编程的两把钥匙
java·后端
程序员清风27 分钟前
ThreadLocal在什么情况下会导OOM?
java·后端·面试
就是帅我不改33 分钟前
基于领域事件驱动的微服务架构设计与实践
后端·面试·架构
JohnYan35 分钟前
Bun技术评估 - 25 Utils(实用工具)
javascript·后端·bun
我要成为Java糕手1 小时前
支付宝芝麻免押支付集成指南及技术对接验收(Java版)
javascript·后端
anthem371 小时前
3、Python持续集成与部署
后端
用户4099322502121 小时前
如何让你的FastAPI Celery Worker在压力下优雅起舞?
后端·github·trae
anthem371 小时前
5、Python文档生成与API设计
后端
ruokkk1 小时前
当你配置了feign.sentinel.enable=true时发生什么
后端·架构