Python入门教程丨2.6 文件操作与 OS 模块,领取你的记账小工具

相信大家肯定遇到这种情况:写了个程序,数据跑得飞快,程序关掉,数据就跟着灰飞烟灭。

比如我想写一个简单的记账系统,让用户记录支出和收入,代码如下:

python 复制代码
data = []

def add_transaction(transaction):
    data.append(transaction)
    print("交易记录成功!")

def view_transactions():
    for i, transaction in enumerate(data, 1):
        print(f"{i}. {transaction}")

def main():
    while True:
        print("\n1. 添加交易记录")
        print("2. 查看交易记录")
        print("0. 退出")
        choice = input("请选择操作:")
        
        if choice == "1":
            transaction = input("请输入交易记录(比如:收入100,支出50):")
            add_transaction(transaction)
        elif choice == "2":
            view_transactions()
        elif choice == "0":
            print("退出程序,数据清零啦!")
            break
        else:
            print("无效选项!")

main()

运行后,我们可以愉快地添加记录、查看记录,直到程序退出。

再次打开程序后,你会发现......记录都不见了!

这是因为程序运行时数据都保存在 **内存**中,一旦终止程序,内存数据都会被释放!


1. 文件操作

为了让数据在程序关闭后也能保留下来,文件 是我们最简单直接的选择,文件是记录在外存(硬盘)中,即便我们关闭程序,文件依然在那里,这样就实现了数据的持久化。

1.1 如何用文件保存数据?

让我们把交易记录保存到一个文件里。

python 复制代码
def add_transaction(transaction):
    with open("交易记录.txt", "a") as f:  # 追加模式
        f.write(transaction + "\n")
    print("交易记录成功!")

def view_transactions():
    print("\n历史交易记录:")
    try:
        with open("transactions.txt", "r") as f:  # 读取模式
            for line in f:
                print(line.strip())
    except FileNotFoundError:
        print("还没有任何记录!")

# 其他代码保持不变

我们使用了文件操作:

使用 **with open**

  • with open("文件路径", "模式") 是文件操作的推荐方式。它会自动管理文件的打开和关闭,即使程序中途出错,也能确保文件被正确关闭。
  • 替代了 f = open(...)f.close() 的繁琐写法。

操作模式

  • "r":只读模式,如果文件不存在会报错。
  • "w":写入模式,会覆盖文件内容。
  • "a":追加模式,保留原有内容,在末尾添加数据。

运行后,程序会把每次记录保存到 交易记录.txt 文件中,下次打开还能查看历史记录。


1.2 文件操作常踩"坑"

程序能跑不代表就万无一失,文件操作也有一些坑:

  1. 文件路径问题
    文件可能不在当前目录,或者路径不对,导致找不到文件。解决方法是:
  • 使用绝对路径。
  • 或者用 os.path 动态构建路径:
lua 复制代码
import os
file_path = os.path.join(os.getcwd(), "transactions.txt")
  1. 文件未关闭

忘记调用 close() 方法,文件可能占用资源。用 with open(...) 结构可以自动关闭文件:

python 复制代码
with open("file.txt", "w") as f:
    f.write("Hello, World!")
  1. 模式选择错误
  • w 模式会覆盖文件内容,容易导致数据丢失。
  • 使用 a 模式追加时,不小心多写了回车符,读取会看到空行。

1.3 为什么需要 os 库?

os 是 Python 操作系统接口的库,提供了丰富的功能:

  • 检查文件是否存在
lua 复制代码
import os
if os.path.exists("transactions.txt"):
    print("文件已存在!")
else:
    print("文件不存在,程序会自动创建。")
  • 创建目录
    如果需要将文件保存在特定目录,先检查目录是否存在,再决定创建:
lua 复制代码
dir_name = "records"
if not os.path.exists(dir_name):
    os.mkdir(dir_name)
print(f"目录 {dir_name} 已创建!")
  • 获取绝对路径
    动态获取当前目录,防止路径错误:
lua 复制代码
print("当前工作目录是:", os.getcwd())

2. 完善记账小工具

之前我们已经讲解了函数和封装,接下来我们就用模块化设计的思想,逐步来完善这个小工具。

2.1. 定义文件存储路径

ini 复制代码
DATA_FILE = "data/accounting.json"

我们先定义了一个文件路径,用于存储和读取数据。文件路径是 "data/accounting.json",其中:

"data/" 是新建的文件夹,用于组织文件。

"accounting.json" 是具体的文件,存储记账数据。我们之前提到过 json,与 python 的字典格式一致,记账软件需要记录各种资产和支出分类,用 json 格式方便管理数据。


2.2. 初始化数据

如果我们是第一次运行程序,就需要定义一个初始数据结构,包含以下内容:

assets:资产账户,记录各账户的余额(初始为 0),可以自定义。

categories:分类,分为收入(income)和支出(expense)。

transactions:交易记录,初始为空列表。

makefile 复制代码
DEFAULT_DATA = {
    "assets": {
        "微信钱包": 0,
        "支付宝": 0,
        "银行账户": 0
    },
    "categories": {
        "income": ["工资", "副业", "其他"],
        "expense": ["日用", "房租", "食品", "娱乐", "交通"]
    },
    "transactions": []
}

我们是第一次运行程序,数据文件不存在,用这个结构初始化数据就能确保程序能正常运行,而不会因为数据缺失而崩溃。


2.3. 加载数据

那假如我们之前运行过呢?那就需要读取已经存在的文件中的数据,而不是重新初始化。

python 复制代码
def load_data():
    """从 JSON 文件加载数据"""
    if not os.path.exists(DATA_FILE):
        os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
        save_data(DEFAULT_DATA)  # 如果文件不存在,创建默认数据
    with open(DATA_FILE, "r", encoding="utf-8") as file:
        return json.load(file)

我们用刚刚提到的 os 库来检查文件是否存在 os.path.exists(DATA_FILE)

  • 如果文件不存在则创建文件夹:os.makedirs,并保存数据:save_data(DEFAULT_DATA)
  • 如果文件存在打开文件读取内容:open(DATA_FILE, "r", encoding="utf-8")。使用 json.load 将 JSON 文件内容加载为 Python 字典。

**这样写可以充分考虑各种情况,**避免数据丢失,例如直接打开一个不存在的文件引发错误;

并能灵活处理文件,如果文件缺失,可以动态创建,而不需要手动创建。


2.4. 保存数据

那么当我们运行结束时,就需要把文件保存

python 复制代码
def save_data(data):
    """将数据保存到 JSON 文件"""
    with open(DATA_FILE, "w", encoding="utf-8") as file:
        json.dump(data, file, ensure_ascii=False, indent=4)

我们先打开文件(写入模式):open(DATA_FILE, "w", encoding="utf-8")。再使用 json.dump 将 Python 数据结构保存为 JSON 格式的字符串,并写入文件。

代码中的ensure_ascii=False可以确保保存的 JSON 文件中可以使用非 ASCII 字符(如中文)。


2.5. 记账逻辑

接下来就是我们基本的处理逻辑了!我们需要记录收入/支出,并要记录交易记录,使用time库来记录时间,并将交易记录与我们的资产关联,收入增加资产,支出减少。

python 复制代码
def add_transaction(data, is_income):
    """记录收入或支出"""
    print("\n请选择资产账户:")
    asset = choose_from_list(list(data["assets"].keys()), "请输入选项编号:")
    if asset is None:
        return

    category_type = "income" if is_income else "expense"
    print(f"\n请选择{'收入' if is_income else '支出'}分类:")
    category = choose_from_list(data["categories"][category_type], "请输入选项编号:")
    if category is None:
        return

    while True:
        try:
            amount = float(input("请输入金额:"))
            if amount <= 0:
                print("错误:金额必须大于 0!")
                continue
            break
        except ValueError:
            print("错误:请输入有效的数字!")

    # 更新资产余额
    if is_income:
        data["assets"][asset] += amount
    else:
        if amount > data["assets"][asset]:
            print("错误:余额不足!")
            return
        data["assets"][asset] -= amount

    # 记录交易
    transaction = {
        "type": "收入" if is_income else "支出",
        "asset": asset,
        "category": category,
        "amount": amount,
        "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")  # 添加时间戳
    }
    data["transactions"].append(transaction)
    print("交易记录成功!")

datetime.now()是记录当前时间的常用函数,方便我们查询记账时间


2.6. 完整代码

最后我们加入交互菜单,再写个循环,就完成了这个记账小工具。

python 复制代码
import os
import json
from datetime import datetime

# 数据文件路径
DATA_FILE = "data/accounting.json"

# 初始化数据
DEFAULT_DATA = {
    "assets": {
        "微信钱包": 0,
        "支付宝": 0,
        "银行账户": 0
    },
    "categories": {
        "income": ["工资", "副业", "其他"],
        "expense": ["日用", "房租", "食品", "娱乐", "交通"]
    },
    "transactions": []
}

def load_data():
    """从 JSON 文件加载数据"""
    if not os.path.exists(DATA_FILE):
        os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
        save_data(DEFAULT_DATA)  # 如果文件不存在,创建默认数据
    with open(DATA_FILE, "r", encoding="utf-8") as file:
        return json.load(file)

def save_data(data):
    """将数据保存到 JSON 文件"""
    with open(DATA_FILE, "w", encoding="utf-8") as file:
        json.dump(data, file, ensure_ascii=False, indent=4)

def display_assets(data):
    """显示所有资产及余额"""
    print("\n当前资产余额:")
    for i, (asset, balance) in enumerate(data["assets"].items(), 1):
        print(f"{i}. {asset}: {balance:.2f} 元")
    print()

def choose_from_list(options, prompt):
    """从选项列表中选择"""
    while True:
        for i, option in enumerate(options, 1):
            print(f"{i}. {option}")
        print("B. 返回上一页")
        choice = input(prompt).strip().upper()
        if choice == "B":
            return None
        if choice.isdigit() and 1 <= int(choice) <= len(options):
            return options[int(choice) - 1]
        print("无效选择,请重试!")

def add_transaction(data, is_income):
    """记录收入或支出"""
    print("\n请选择资产账户:")
    asset = choose_from_list(list(data["assets"].keys()), "请输入选项编号:")
    if asset is None:
        return

    category_type = "income" if is_income else "expense"
    print(f"\n请选择{'收入' if is_income else '支出'}分类:")
    category = choose_from_list(data["categories"][category_type], "请输入选项编号:")
    if category is None:
        return

    while True:
        try:
            amount = float(input("请输入金额:"))
            if amount <= 0:
                print("错误:金额必须大于 0!")
                continue
            break
        except ValueError:
            print("错误:请输入有效的数字!")

    # 更新资产余额
    if is_income:
        data["assets"][asset] += amount
    else:
        if amount > data["assets"][asset]:
            print("错误:余额不足!")
            return
        data["assets"][asset] -= amount

    # 记录交易
    transaction = {
        "type": "收入" if is_income else "支出",
        "asset": asset,
        "category": category,
        "amount": amount,
        "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")  # 添加时间戳
    }
    data["transactions"].append(transaction)
    print("交易记录成功!")

def add_asset(data):
    """新增资产账户"""
    asset_name = input("请输入新资产名称:")
    if asset_name in data["assets"]:
        print("错误:资产已存在!")
        return
    try:
        initial_balance = float(input("请输入初始余额:"))
    except ValueError:
        print("错误:请输入有效的数字!")
        return

    data["assets"][asset_name] = initial_balance
    print("资产新增成功!")

def add_category(data, is_income):
    """新增分类"""
    category_type = "income" if is_income else "expense"
    new_category = input(f"请输入新{'收入' if is_income else '支出'}分类名称:")
    if new_category in data["categories"][category_type]:
        print("错误:分类已存在!")
        return
    data["categories"][category_type].append(new_category)
    print("分类新增成功!")

def view_transactions(data):
    """查看交易记录"""
    print("\n历史交易记录:")
    if not data["transactions"]:
        print("暂无记录。")
    for i, t in enumerate(data["transactions"], 1):
        print(f"{i}. [{t['type']}] 时间: {t['time']}, 账户: {t['asset']}, 分类: {t['category']}, 金额: {t['amount']:.2f} 元")
    print()

def main():
    """主函数"""
    data = load_data()

    while True:
        print("\n======== 记账软件菜单 ========")
        print("1. 查看资产余额")
        print("2. 记录收入")
        print("3. 记录支出")
        print("4. 新增资产账户")
        print("5. 新增收入分类")
        print("6. 新增支出分类")
        print("7. 查看交易记录")
        print("0. 退出程序")
        choice = input("请选择操作:").strip()

        if choice == "1":
            display_assets(data)
        elif choice == "2":
            add_transaction(data, is_income=True)
        elif choice == "3":
            add_transaction(data, is_income=False)
        elif choice == "4":
            add_asset(data)
        elif choice == "5":
            add_category(data, is_income=True)
        elif choice == "6":
            add_category(data, is_income=False)
        elif choice == "7":
            view_transactions(data)
        elif choice == "0":
            save_data(data)
            print("数据已保存,感谢使用!")
            break
        else:
            print("无效选项,请重试!")

if __name__ == "__main__":
    main()

运行截图:

3. 小结

在本节中我们使用文件的方式来实现数据的持久化💾,主要需要学习了文件的相关操作,我们简单回顾一下。

文件操作

  1. 使用 open 打开文件,可选择模式(rwa 等)。
  2. 常用方法包括 readwritereadlinewritelines
  3. JSON 文件操作使用 json.dumpjson.load

**os** 库功能

  1. 路径操作:检查路径是否存在、拼接路径、获取目录部分等。
  2. 文件夹操作:创建多级文件夹、删除空文件夹。
  3. 文件操作:删除文件、重命名文件或文件夹。
相关推荐
Hacker_Oldv4 分钟前
Python 爬虫与网络安全有什么关系
爬虫·python·web安全
深蓝海拓11 分钟前
PySide(PyQT)重新定义contextMenuEvent()实现鼠标右键弹出菜单
开发语言·python·pyqt
数据攻城小狮子2 小时前
深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配
图像处理·python·opencv·算法·计算机视觉
ONE_PUNCH_Ge2 小时前
Python 爬虫 – BeautifulSoup
python
L_cl3 小时前
【Python 数据结构 1.零基础复习】
数据结构·python
Monkey_Jun3 小时前
《Python百练成仙》31-40章(不定时更新)
开发语言·python
没事偷着乐琅3 小时前
人工智能 pytorch篇
人工智能·pytorch·python
Python数据分析与机器学习3 小时前
《基于Django和ElasticSearch的学术论文搜索推荐系统的设计与实现》开题报告
大数据·开发语言·python·elasticsearch·搜索引擎·django·课程设计
邪恶的贝利亚3 小时前
Pytorch常用函数
人工智能·pytorch·python