Python实战小游戏(三): 简易文件管理器

引言

Python入门到精通(三)中,我们了解Python模块、输入输出以及文件读写操作。

现在编写一个小游戏对文件读写、输入输出进行简单应用,巩固基础,加深理解。

文章目录

简易文件管理器说明

一、主要功能

实现了一个基于命令行的简单文件管理器。

主要功能包括:

1)文件/目录浏览 - 显示当前目录内容

2)目录导航 - 切换目录、返回上一级

3)文件操作 - 创建文件/目录、删除项目

4)搜索功能 - 在目录树中搜索文件/目录

5)书签系统 - 保存常用目录路径

6)配置持久化 - 保存书签到JSON文件

7)历史记录 - 记录访问过的目录(最多10条)

二、实现思路和步骤

2.1 整体架构

面向对象设计:使用SimpleFileManager类封装所有功能

配置管理:使用JSON文件存储书签等配置信息

命令行交互:通过简单的命令语法进行用户交互

路径管理:使用pathlib.Path处理跨平台路径问题

2.2 核心实现步骤

1)初始化阶段

· 设置基础工作目录

· 初始化历史记录和书签字典

· 加载现有配置文件

python 复制代码
def __init__(self, base_dir="."):
	# 初始化基础目录,确保是绝对路径
	self.base_dir = Path(base_dir).resolve()
	# 历史记录栈,用于返回上一级目录
	self.history = []
	# 书签字典:名称->路径
	self.bookmarks = {}
	# 加载已有配置
	self.load_config()
	
def load_config(self):
	config_file = self.base_dir / ".file_manager.json" # /运算符在路径操作中用于连接路径
	if config_file.exists(): # 如果文件存在
		try:
			# 读取JSON配置
			with open(config_file, "r", encoding="utf-8") as f:
			config = json.load(f)
			# 获取书签配置
			self.bookmarks = config.get("bookmarks", {})
		except Exception as e:
			print(f"加载配置失败: {e}")

2)文件系统操作

· 使用os.walk()进行目录遍历

· 使用pathlib模块方法来删除文件

· 使用shutil进行递归删除操作

python 复制代码
# 在当前目录树中搜索文件/目录
def search_files(self, pattern: str):
	matches = [] # 符合检索的文件/目录
	pattern_lower = pattern.lower()  # 不区分大小写搜索
	# 递归遍历目录树
	for root, dirs, files in os.walk(self.base_dir):
		# 排除隐藏目录(不搜索隐藏目录)
		dirs[:] = [d for d in dirs if not d.startswith(".")] #目录名以.开头的为隐藏目录
		# 搜索文件和目录名
		for name in files + dirs:
			if pattern_lower in name.lower():
				full_path = Path(root) / name
				# 计算相对于当前目录的路径
				rel_path = full_path.relative_to(self.base_dir)
				file_type = "目录" if full_path.is_dir() else "文件"
				matches.append({
					"name": str(rel_path),
					"type": file_type
				})
		# 显示搜索结果
		if matches:
			print(f"\n找到 {len(matches)} 个匹配项:")
			for match in matches[:20]:  # 最多显示20个结果
				print(f"[{match['type']}] {match['name']}")
			if len(matches) > 20:
				print(f"... 还有 {len(matches) - 20} 个结果未显示")
			else:
				print("未找到匹配项")

# 删除文件或目录
def delete_item(self, name: str, confirm=True):
	target = self.base_dir / name
	if not target.exists():
		print(f"项目不存在: {name}")
		return False
	# 确认删除(如果需要)
	if confirm:
		confirm = input(f"确定删除 {name} 吗?(y/N): ").strip().lower()
		if confirm != "y":
			print("取消删除")
			return False
		try:
			# 递归删除目录或删除文件
			if target.is_dir(): #is_dir()和unlink()方法属于pathlib模块方法
				shutil.rmtree(target) # 递归删除整个目录树
			else:
				target.unlink() # 删除文件
			print(f"已删除: {name}")
			return True
		except Exception as e:
			print(f"删除失败: {e}")
			return False

3)用户界面

· 实时显示当前目录路径

· 格式化输出文件信息(名称、类型、大小、修改时间)

· 提供简洁的输入提示

python 复制代码
# 格式化显示文件列表
def display_files(self, files: List[Dict[str, Any]]):
   print(f"\n当前目录: {self.base_dir}")
   print("=" * 80)
   # 表头
   print(f"{'名称':<30} {'类型':<10} {'大小':<10} {'修改时间':<20}")
   print("-" * 80)
   # 显示每个文件的信息
   for file in files:
       size_str = self.format_size(file["size"])
       time_str = file["modified"].strftime("%Y-%m-%d %H:%M:%S")
       print(f"{file['name']:<30} {file['type']:<10} {size_str:<10} {time_str:<20}")
   print("=" * 80)
   print(f"总计: {len(files)} 个项目")        

4)持久化存储

· 使用JSON格式存储书签配置

· 自动保存到隐藏的配置文件.file_manager.json

python 复制代码
# 保存配置到JSON文件
def save_config(self):
  config_file = self.base_dir / ".file_manager.json"
  config = {"bookmarks": self.bookmarks}
  try:
      with open(config_file, "w", encoding="utf-8") as f:
        # 格式化保存JSON,确保中文正常显示
        json.dump(config, f, indent=2, ensure_ascii=False)
  except Exception as e:
      print(f"保存配置失败: {e}")

三、代码实现及注释

python 复制代码
import os
import shutil
import json
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Any
# 简易文件管理器类
class SimpleFileManager:
    def __init__(self, base_dir="."):
        # 初始化基础目录,确保是绝对路径
        self.base_dir = Path(base_dir).resolve()
        # 历史记录栈,用于返回上一级目录
        self.history = []
        # 书签字典:名称->路径
        self.bookmarks = {}
        # 加载已有配置
        self.load_config()
    # 加载配置文件
    def load_config(self):
        config_file = self.base_dir / ".file_manager.json"
        if config_file.exists():
            try:
                # 读取JSON配置
                with open(config_file, "r", encoding="utf-8") as f:
                    config = json.load(f)
                    # 获取书签配置
                    self.bookmarks = config.get("bookmarks", {})
            except Exception as e:
                print(f"加载配置失败: {e}")
    # 保存配置到JSON文件
    def save_config(self):
        config_file = self.base_dir / ".file_manager.json"
        config = {"bookmarks": self.bookmarks}
        try:
            with open(config_file, "w", encoding="utf-8") as f:
                # 格式化保存JSON,确保中文正常显示
                json.dump(config, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存配置失败: {e}")
    # 获取当前目录下的文件和目录列表
    def get_current_files(self, show_hidden=False) -> List[Dict[str, Any]]:
        files = []
        # 遍历当前目录
        for item in self.base_dir.iterdir():
            # 根据参数决定是否显示隐藏文件
            if not show_hidden and item.name.startswith("."):
                continue
            try:
                # 获取文件状态信息
                stat = item.stat()
                file_info = {
                    "name": item.name,
                    "type": "目录" if item.is_dir() else "文件",
                    "size": stat.st_size,
                    "modified": datetime.fromtimestamp(stat.st_mtime),
                    "path": str(item)
                }
                files.append(file_info)
            except OSError:
                # 忽略权限不足等错误
                continue
        # 排序:目录在前,文件在后,按名称不区分大小写排序
        files.sort(key=lambda x: (x["type"] != "目录", x["name"].lower()))
        return files
    # 格式化显示文件列表
    def display_files(self, files: List[Dict[str, Any]]):
        print(f"\n当前目录: {self.base_dir}")
        print("=" * 80)
        # 表头
        print(f"{'名称':<30} {'类型':<10} {'大小':<10} {'修改时间':<20}")
        print("-" * 80)
        # 显示每个文件的信息
        for file in files:
            size_str = self.format_size(file["size"])
            time_str = file["modified"].strftime("%Y-%m-%d %H:%M:%S")
            print(f"{file['name']:<30} {file['type']:<10} {size_str:<10} {time_str:<20}") 
        print("=" * 80)
        print(f"总计: {len(files)} 个项目")
    # 将字节数转换为易读的大小表示
    def format_size(self, size_bytes: int) -> str:
        for unit in ["B", "KB", "MB", "GB", "TB"]:
            if size_bytes < 1024.0:
                return f"{size_bytes:.1f}{unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.1f}PB"
    # 切换到指定目录
    def navigate(self, target: str):
        # 处理相对路径
        target_path = (self.base_dir / target).resolve()
        # 特殊处理:返回上级目录
        if target == "..":
            target_path = self.base_dir.parent
        # 验证目录存在且可访问
        if target_path.exists() and target_path.is_dir():
            # 保存当前目录到历史记录
            self.history.append(str(self.base_dir))
            # 限制历史记录长度
            if len(self.history) > 10:
                self.history.pop(0)
            # 更新当前目录
            self.base_dir = target_path
            return True
        else:
            print(f"目录不存在或无权限: {target}")
            return False
    # 返回到历史记录中的上一个目录
    def go_back(self):
        if self.history:
            self.base_dir = Path(self.history.pop())
            return True
        return False
    # 创建空文件
    def create_file(self, filename: str):
        """"""
        filepath = self.base_dir / filename
        try:
            filepath.touch()  # 创建文件
            print(f"文件已创建: {filename}")
        except Exception as e:
            print(f"创建文件失败: {e}")
    # 创建目录
    def create_dir(self, dirname: str):
        dirpath = self.base_dir / dirname
        try:
            dirpath.mkdir(exist_ok=True)  # 如果已存在则不报错
            print(f"目录已创建: {dirname}")
        except Exception as e:
            print(f"创建目录失败: {e}")
    # 删除文件或目录
    def delete_item(self, name: str, confirm=True):
        target = self.base_dir / name
        if not target.exists():
            print(f"项目不存在: {name}")
            return False
        # 确认删除(如果需要)
        if confirm:
            confirm = input(f"确定删除 {name} 吗?(y/N): ").strip().lower()
            if confirm != "y":
                print("取消删除")
                return False
        try:
            # 递归删除目录或删除文件
            if target.is_dir():
                shutil.rmtree(target)
            else:
                target.unlink()
            print(f"已删除: {name}")
            return True
        except Exception as e:
            print(f"删除失败: {e}")
            return False
    # 添加书签:将当前目录或指定路径保存为书签
    def add_bookmark(self, name: str, path: str = None):
        if path is None:
            path = str(self.base_dir)
        self.bookmarks[name] = path
        self.save_config()  # 保存到配置文件
        print(f"书签已添加: {name} -> {path}")
    # 跳转到书签指向的目录
    def jump_to_bookmark(self, name: str):
        if name in self.bookmarks:
            target = Path(self.bookmarks[name])
            if target.exists() and target.is_dir():
                # 保存当前目录到历史记录
                self.history.append(str(self.base_dir))
                self.base_dir = target
                print(f"已跳转到书签: {name}")
                return True
            else:
                print(f"书签指向的目录不存在: {self.bookmarks[name]}")
        else:
            print(f"书签不存在: {name}")
        return False
    # 显示所有书签
    def show_bookmarks(self):
        if not self.bookmarks:
            print("暂无书签")
            return
        print("\n书签列表:")
        for name, path in self.bookmarks.items():
            print(f"  {name}: {path}")
    # 在当前目录树中搜索文件/目录
    def search_files(self, pattern: str):
        matches = []
        pattern_lower = pattern.lower()  # 不区分大小写搜索
        # 递归遍历目录树
        for root, dirs, files in os.walk(self.base_dir):
            # 排除隐藏目录(不搜索隐藏目录)
            dirs[:] = [d for d in dirs if not d.startswith(".")]
            # 搜索文件和目录名
            for name in files + dirs:
                if pattern_lower in name.lower():
                    full_path = Path(root) / name
                    # 计算相对于当前目录的路径
                    rel_path = full_path.relative_to(self.base_dir)
                    
                    file_type = "目录" if full_path.is_dir() else "文件"
                    matches.append({
                        "name": str(rel_path),
                        "type": file_type
                    })
        # 显示搜索结果
        if matches:
            print(f"\n找到 {len(matches)} 个匹配项:")
            for match in matches[:20]:  # 最多显示20个结果
                print(f"  [{match['type']}] {match['name']}")
            if len(matches) > 20:
                print(f"  ... 还有 {len(matches) - 20} 个结果未显示")
        else:
            print("未找到匹配项")
    
    # 运行文件管理器主循环
    def run(self):
        print("简易文件管理器")
        print("命令列表:")
        print("  ls [a]          - 列出文件 (a显示隐藏文件)")
        print("  cd <目录>       - 切换目录")
        print("  back            - 返回上一级")
        print("  mkdir <名称>    - 创建目录")
        print("  touch <名称>    - 创建文件")
        print("  rm <名称>       - 删除项目")
        print("  search <关键词> - 搜索文件")
        print("  bookmark add <名称> [路径] - 添加书签")
        print("  bookmark go <名称>        - 跳转到书签")
        print("  bookmark list            - 显示书签")
        print("  exit            - 退出")
        
        # 主命令循环
        while True:
            try:
                # 显示当前目录提示符
                command = input(f"\n{self.base_dir}> ").strip()
                if not command:
                    continue 
                # 解析命令和参数
                parts = command.split()
                cmd = parts[0].lower()
                args = parts[1:]
                # 命令分发
                if cmd == "ls":
                    show_hidden = len(args) > 0 and args[0] == "a"
                    files = self.get_current_files(show_hidden)
                    self.display_files(files)
                elif cmd == "cd":
                    if args:
                        self.navigate(args[0])
                    else:
                        print("用法: cd <目录>")
                elif cmd == "back":
                    if not self.go_back():
                        print("无法返回")
                elif cmd == "mkdir":
                    if args:
                        self.create_dir(args[0])
                    else:
                        print("用法: mkdir <目录名>")
                elif cmd == "touch":
                    if args:
                        self.create_file(args[0])
                    else:
                        print("用法: touch <文件名>")
                elif cmd == "rm":
                    if args:
                        confirm = len(args) > 1 and args[1] == "-y"
                        self.delete_item(args[0], not confirm)
                    else:
                        print("用法: rm <名称> [-y]")
                elif cmd == "search":
                    if args:
                        self.search_files(" ".join(args))
                    else:
                        print("用法: search <关键词>")
                elif cmd == "bookmark":
                    # 书签子命令处理
                    if len(args) >= 2:
                        subcmd = args[0]
                        if subcmd == "add":
                            name = args[1]
                            path = args[2] if len(args) > 2 else None
                            self.add_bookmark(name, path)
                        elif subcmd == "go":
                            self.jump_to_bookmark(args[1])
                        else:
                            print("未知的书签子命令")
                    elif args and args[0] == "list":
                        self.show_bookmarks()
                    else:
                        print("书签命令用法:")
                        print("  bookmark add <名称> [路径]")
                        print("  bookmark go <名称>")
                        print("  bookmark list")
                elif cmd in ["exit", "quit"]:
                    print("再见!")
                    break
                else:
                    print(f"未知命令: {cmd}")
            except KeyboardInterrupt:
                print("\n\n再见!")
                break
            except Exception as e:
                print(f"发生错误: {e}")

# 程序入口
if __name__ == "__main__":
    # 创建文件管理器实例并运行
    manager = SimpleFileManager()
    manager.run()

其他

os.walk函数

os.walk()是 Python 文件系统操作中最常用、最强大的工具之一,几乎所有的文件遍历需求都可以用它解决。

os.walk(top, topdown=True, onerror=None, followlinks=False) 返回一个三元组{dirpath:字符串,当前正在遍历的目录路径,dirnames:列表,当前目录下的所有子目录,filenames:列表,当前目录下的所有非目录文件名}

pathlib模块

pathlib 是 Python 3.4+ 中用于处理文件路径的现代化模块,比传统的 os.path 更好用、更直观。

pathlib.Path 对象一个智能的路径对象,把文件/文件夹路径变成了可以操作的对象。

其中的is_dir() 方法用于检查 target 路径是否指向一个目录,unlink()方法用于删除 target 路径所指向的文件。

shutil模块

shutil是Python中用于高级文件操作的模块,提供了对文件和文件夹进行复制、移动、删除等操作的功能,比基本的os模块更强大。其rmtree方法常用于递归删除整个目录树,包括所有子目录和文件。它类似于命令行中的 rm -rf 命令。

shutil.rmtree(path, ignore_errors=False, onerror=None, dir_fd=None) 要谨慎使用,因为会删除整个目录树,且不可恢复

相关推荐
笨鸟先飞,勤能补拙3 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能3 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂4 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study4 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境5 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境5 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1235 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102166 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb006 小时前
一个项目带你入门AI应用开发01
python
0566466 小时前
Python康复训练——常用标准库
开发语言·python·学习