Python实战第9期:文件操作

引言:为什么需要文件操作?

在前面的课程中,我们处理的数据都是存储在内存中的。程序一旦结束,数据就消失了。

但如果我们需要:

  • 保存程序运行的结果
  • 读取外部配置文件
  • 处理大量的数据文件
  • 生成报告文件

这时候就需要用到文件操作。通过文件操作,我们可以将数据持久化保存到磁盘,也可以从磁盘读取数据。

文件操作是程序与外部世界交互的重要方式,掌握了文件操作,你的程序就能处理真实世界的数据了。


一、文件的打开和关闭

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("文件不可读")

八、本篇小结

今天,我们完成了以下学习内容:

  1. 文件的打开和关闭:

    • 使用 open() 打开文件
    • 文件打开模式:r、w、a、x、b、+
    • 使用 with 语句自动关闭文件
  2. 读写文本文件:

    • 读取:read()、readline()、readlines()
    • 写入:write()、writelines()
    • 文件位置:tell()、seek()
  3. 读写CSV文件:

    • 使用 csv 模块
    • reader() 和 writer()
    • DictReader() 和 DictWriter()
  4. 读写JSON文件:

    • 使用 json 模块
    • load() 和 dump() 用于文件
    • loads() 和 dumps() 用于字符串
  5. 文件路径处理:

    • 使用 os.path 模块
    • 使用 pathlib 模块(推荐)
    • 路径拼接、判断、遍历

请记住这三个结论:

  1. 始终使用with语句:确保文件被正确关闭
  2. 指定编码 :读写文本文件时指定 encoding="utf-8"
  3. pathlib更强大 :推荐使用 pathlib 处理文件路径

下期预告

在第10期中,我们将学习Python的异常处理,包括:

  • 异常的概念和类型
  • try-except语句
  • 自定义异常
  • 异常处理的最佳实践

敬请期待!


✍坚持原创,求关注,点赞,收藏

相关推荐
小星星_ios1 小时前
从 3Blue1Brown 到 NumPy:手写一个神经网络训练 MNIST
人工智能·python·神经网络
神魔皆以血嗜1 小时前
Java 实现简单状态机(订单状态流转)
java·后端
Maxwin1 小时前
Manim 竖屏画布选 9×16 还是 4.5×8?从 font_size 和每单位像素数说起
python·ffmpeg
zz在掘金337081 小时前
MongoDB 脚本速查
后端
随性而行3601 小时前
企业微信 API 如何实现业务消息自动通知?接口应用场景解析
java·人工智能·后端·微信·机器人·企业微信
Mav1 小时前
[个人学习记录]从零构建高性能 LLM 推理网关:长连接优雅停机(Graceful Drain)、K8s 探针联动与 Context 底层哲学
后端
昭昭日月明1 小时前
使用 Ragas 量化 RAG
python·langchain·ai编程
团子哒哒1 小时前
变量、常量和数据类型
后端
zz在掘金337081 小时前
Redis 脚本速查
后端