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 小时前
【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)
python·测试工具·django·接口测试·postman·fastapi·api项目
winfredzhang3 小时前
wxPython实战:打造一个优雅的图片预览工具
chrome·python·预览·剪切板
superman超哥3 小时前
仓颉GC调优参数深度解析
c语言·开发语言·c++·python·仓颉
Byron Loong3 小时前
【机器视觉】人物安全距离监测
python·yolo·计算机视觉
Swizard3 小时前
告别“裸奔”代码:用 Pydantic 让你的 Python 数据固若金汤
python
老歌老听老掉牙3 小时前
砂轮轮廓的数学建模与可视化分析
python·数学建模·sympy·砂轮
xoliu13 小时前
Pytorch核心基础入门
人工智能·pytorch·python
一瞬祈望3 小时前
ResNet50 图像分类完整实战(Notebook Demo + 训练代码)
人工智能·python·神经网络·数据挖掘