day-030-综合练习-笔记管理器

Day 30:综合练习------个人笔记管理器 & 第一阶段总结

第一阶段最后一天,做一个完整的笔记管理器,然后回顾这 30 天我们学到了什么。


一、笔记管理器------功能需求

  • 创建 Markdown 笔记(自动生成文件名)
  • 列出所有笔记
  • 按关键词搜索笔记
  • 查看/编辑笔记
  • 给笔记加标签
  • 按标签筛选
  • 所有笔记存储在本地文件夹中

二、完整代码

python 复制代码
"""
个人笔记管理器
笔记存储在 ./notes 文件夹中,元数据存储在 notes.json
"""
import json
import re
from datetime import datetime
from pathlib import Path


class Note:
    """一篇笔记"""
    def __init__(self, title, content="", tags=None):
        self.title = title
        self.content = content
        self.tags = tags or []
        self.created_at = datetime.now().isoformat()
        self.updated_at = self.created_at
        self.filename = self._make_filename(title)

    @staticmethod
    def _make_filename(title):
        """把标题转成安全的文件名"""
        safe = re.sub(r"[^\w\s-]", "", title)      # 去掉特殊字符
        safe = re.sub(r"\s+", "_", safe.strip())    # 空格转下划线
        return f"{safe}.md"

    def update_content(self, content):
        self.content = content
        self.updated_at = datetime.now().isoformat()

    def add_tag(self, tag):
        if tag not in self.tags:
            self.tags.append(tag)

    def matches_search(self, keyword):
        """搜索标题和内容"""
        kw = keyword.lower()
        return kw in self.title.lower() or kw in self.content.lower()

    def to_dict(self):
        return {
            "title": self.title,
            "tags": self.tags,
            "filename": self.filename,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
        }

    @classmethod
    def from_dict(cls, data, content=""):
        note = cls(data["title"], content, data.get("tags", []))
        note.filename = data["filename"]
        note.created_at = data["created_at"]
        note.updated_at = data.get("updated_at", data["created_at"])
        return note


class NoteManager:
    """笔记管理器"""
    def __init__(self, notes_dir="notes"):
        self.notes_dir = Path(notes_dir)
        self.meta_file = self.notes_dir / "notes.json"
        self.notes = []    # Note 对象列表
        self._init_storage()
        self.load()

    def _init_storage(self):
        self.notes_dir.mkdir(exist_ok=True)
        if not self.meta_file.exists():
            self.meta_file.write_text('{"notes": []}', encoding="utf-8")

    def load(self):
        """加载元数据和笔记内容"""
        try:
            meta = json.loads(self.meta_file.read_text(encoding="utf-8"))
            for item in meta.get("notes", []):
                note_file = self.notes_dir / item["filename"]
                content = note_file.read_text(encoding="utf-8") if note_file.exists() else ""
                self.notes.append(Note.from_dict(item, content))
        except (json.JSONDecodeError, KeyError):
            self.notes = []

    def save(self):
        """保存元数据"""
        meta = {"notes": [n.to_dict() for n in self.notes]}
        self.meta_file.write_text(
            json.dumps(meta, ensure_ascii=False, indent=2),
            encoding="utf-8",
        )

    def create(self, title, content=""):
        """创建笔记"""
        note = Note(title, content)
        self.notes.append(note)
        self._write_note_file(note)
        self.save()
        print(f"创建笔记:{note.title}")
        return note

    def _write_note_file(self, note):
        """把笔记内容写入文件"""
        filepath = self.notes_dir / note.filename
        filepath.write_text(note.content, encoding="utf-8")

    def list_all(self):
        """列出所有笔记"""
        if not self.notes:
            print("还没有笔记,来创建第一篇吧!")
            return
        print(f"\n共 {len(self.notes)} 篇笔记:")
        for i, note in enumerate(self.notes, 1):
            tags = f"[{', '.join(note.tags)}]" if note.tags else ""
            preview = note.content[:40].replace("\n", " ") + ("..." if len(note.content) > 40 else "")
            print(f"  {i}. {note.title} {tags}")
            if preview:
                print(f"     {preview}")

    def search(self, keyword):
        """搜索笔记"""
        results = [n for n in self.notes if n.matches_search(keyword)]
        if not results:
            print(f"没有找到包含 '{keyword}' 的笔记")
            return
        print(f"\n找到 {len(results)} 篇包含 '{keyword}' 的笔记:")
        for note in results:
            print(f"  - {note.title}")

    def view(self, title_or_index):
        """查看笔记"""
        note = self._find_note(title_or_index)
        if not note:
            return
        print(f"\n{'=' * 50}")
        print(f"标题:{note.title}")
        print(f"标签:{', '.join(note.tags) if note.tags else '无'}")
        print(f"更新:{note.updated_at[:19]}")
        print(f"{'=' * 50}")
        print(note.content if note.content else "(空内容)")
        print("=" * 50)

    def edit(self, title_or_index):
        """编辑笔记"""
        note = self._find_note(title_or_index)
        if not note:
            return
        print(f"正在编辑:{note.title}")
        print("输入新内容(输入 END 结束):")
        lines = []
        while True:
            line = input()
            if line == "END":
                break
            lines.append(line)
        note.update_content("\n".join(lines))
        self._write_note_file(note)
        self.save()
        print("笔记已更新!")

    def _find_note(self, key):
        """按标题或序号查找笔记"""
        for note in self.notes:
            if note.title == key:
                return note
        try:
            idx = int(key) - 1
            if 0 <= idx < len(self.notes):
                return self.notes[idx]
        except ValueError:
            pass
        print(f"找不到笔记:{key}")
        return None

    def run(self):
        """主循环"""
        while True:
            print("\n" + "=" * 50)
            print("📝 笔记管理器")
            print("=" * 50)
            print("  1. 新建笔记  2. 查看列表  3. 搜索")
            print("  4. 查看内容  5. 编辑内容  6. 退出")
            print("-" * 50)

            choice = input("选择:").strip()
            try:
                if choice == "1":
                    title = input("标题:")
                    self.create(title)
                elif choice == "2":
                    self.list_all()
                elif choice == "3":
                    kw = input("搜索关键词:")
                    self.search(kw)
                elif choice == "4":
                    self.view(input("标题或序号:"))
                elif choice == "5":
                    self.edit(input("标题或序号:"))
                elif choice == "6":
                    print("再见!")
                    break
            except Exception as e:
                print(f"出错:{e}")


if __name__ == "__main__":
    NoteManager().run()

三、第一阶段回顾

30 天前的我们 vs 现在的我们:

阶段 天数 核心内容
基础语法 Day 1-12 变量、数据类型、字符串、列表、字典、集合、条件、循环
函数 Day 13-15 函数定义、参数、作用域、lambda
文件操作 Day 16-18 文件读写、CSV、JSON、pathlib
错误处理 Day 19-21 try/except、raise、调试技巧
模块与包 Day 22-24 import、包结构、pip、标准库、第三方库
面向对象 Day 25-27 类与对象、继承、封装、多态、特殊方法
综合实战 Day 28-30 Todo应用、数据分析工具、笔记管理器

从一行 print("Hello World") 到自己写出完整的命令行应用,这就是 30 天的力量。


四、明天学什么?

第一阶段 Python 基础结束。明天正式进入第二阶段:数据科学工具 ------从 NumPy 开始。NumPy 是整个 Python 数据科学和 AI 生态的基石。


30天的积累,你已经不是"零基础"了。后面的路是站在这个基础上往上走------学 NumPy、Pandas,然后走进机器学习和深度学习的大门。
第30天,打卡完成。里程碑达成!


本系列是个人学习笔记,如有错误欢迎在评论区指正交流。

相关推荐
乖巧的妹子1 小时前
Python基础核心知识点详解:内置函数、运算符、字符串方法、数据结构与类型转换
python
幸福清风2 小时前
Python 完美处理Excel合并单元格:拆分填充+自动合并
python·excel·合并单元格·拆分单元格
云空2 小时前
《Three.js 3D实例大全》
开发语言·javascript·3d·three.js
techdashen2 小时前
Go 1.26 新增 `bytes.Buffer.Peek`:只看数据,不移动读取位置
开发语言·后端·golang
汤姆小白2 小时前
08-应用部署
人工智能·python·机器学习·numpy·transformer
C137的本贾尼3 小时前
第七篇:消息队列(MQ)——就是个带存储的异步通信管道
java·开发语言·中间件
从零开始的代码生活_3 小时前
C++ list 原理与实践:双向链表、迭代器与简化实现
开发语言·c++·后端·学习·算法·链表·list
hy.z_7774 小时前
【C语言】11. 深入理解指针1
c语言·开发语言
charlie1145141914 小时前
现代C++工程实践——WeakPtr 第二关:核心骨架与控制块
开发语言·c++