Skip to content

File Tool

File Tool

做网站的时候用到的莫名其妙妙妙工具,感觉以后说不定有用,存一下。

md文件分割

按H1标题对Markdown文件进行分割成多个文件并输出。

使用要求是写的时候排版比较规范。

点击这里查看 Python 源码
import os
import re


def split_markdown_by_h1(source_file, output_dir="output"):
    """
    读取 Markdown 文件并根据 H1 标签切割成多个文件。
    文件名将基于 H1 的标题内容。
    """

    # 确保输出目录存在
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
        print(f"已创建输出目录: {output_dir}")

    try:
        with open(source_file, 'r', encoding='utf-8') as f:
            lines = f.readlines()
    except FileNotFoundError:
        print(f"错误: 找不到文件 {source_file}")
        return

    current_file = None
    file_count = 0

    # 用于处理第一个 H1 之前的内容(前言/Preamble)
    # 如果不需要保留前言,可以将 initial_content 设为 None
    preamble_filename = os.path.join(output_dir, "00_preamble.md")
    current_file = open(preamble_filename, 'w', encoding='utf-8')
    is_writing_preamble = True

    for line in lines:
        # 检测 H1 标签 (以 # 开头,后跟空格)
        if line.startswith('# '):
            # 关闭上一个文件
            if current_file:
                current_file.close()
                # 如果前言文件为空,删除它
                if is_writing_preamble and os.path.getsize(preamble_filename) == 0:
                    os.remove(preamble_filename)
                is_writing_preamble = False

            # 提取标题内容并清洗非法字符
            title = line.strip('# \n').strip()
            # 替换文件名中的非法字符 (Windows/Linux/Mac 通用)
            safe_title = re.sub()   //  这里的参数是 r'[\\/*?:"<>|]', "_", title  但放在括号里会导致md的逻辑出现问题所以放在注释了

            filename = f"{safe_title}.md"
            file_path = os.path.join(output_dir, filename)

            print(f"正在生成: {filename}")
            current_file = open(file_path, 'w', encoding='utf-8')
            file_count += 1

            # 写入标题行(如果不想在新文件中保留标题,注释掉下面这行)
            current_file.write(line)
        else:
            # 写入内容到当前打开的文件
            if current_file:
                current_file.write(line)

    # 关闭最后一个文件
    if current_file:
        current_file.close()
        # 再次检查前言是否为空(如果是整个文件都没有 H1 的情况)
        if is_writing_preamble and os.path.getsize(preamble_filename) == 0:
            os.remove(preamble_filename)

    print(f"\n处理完成!共生成 {file_count} 个基于 H1 的文件。")


## --- 使用示例 ---
if __name__ == "__main__":
    # 将此处替换为你的文件名
    source_md = "CSS.md"
    split_markdown_by_h1(source_md)

json格式文件树生成器

读取已有目录递归地生成固定格式的json文件,也可以直接复制json代码过来调整和拼接,可视化看着比纯代码方便。

主要用于生成网站要用的文件树配置文件。

点击这里查看 Python 源码
import os
import json
import tkinter as tk
from tkinter import ttk, filedialog, messagebox


class DirectoryMapperApp:
    def __init__(self, root):
        self.root = root
        self.root.title("目录结构生成器 (Directory to JSON)")
        self.root.geometry("1100x700")  # 增加窗口尺寸以适应双栏布局

        # 数据存储
        self.current_root_path = ""
        self.drag_source = None  # 用于存储当前正在拖拽的项

        # --- UI 布局 ---

        # 1. 顶部按钮区
        top_frame = tk.Frame(root, pady=10)
        top_frame.pack(fill=tk.X, padx=10)

        self.btn_load = ttk.Button(top_frame, text="1. 选择文件夹", command=self.select_directory)
        self.btn_load.pack(side=tk.LEFT, padx=5)

        self.btn_save = ttk.Button(top_frame, text="2. 导出 JSON 文件", command=self.save_json, state=tk.DISABLED)
        self.btn_save.pack(side=tk.LEFT, padx=5)

        self.lbl_path = tk.Label(top_frame, text="未选择文件夹 (可直接在右侧粘贴 JSON 生成)", fg="gray")
        self.lbl_path.pack(side=tk.LEFT, padx=10)

        # 2. 中间主内容区 (使用 PanedWindow 实现左右分栏可调整)
        self.paned_window = ttk.PanedWindow(root, orient=tk.HORIZONTAL)
        self.paned_window.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # --- 左侧:树状视图 ---
        left_frame = tk.Frame(self.paned_window)
        self.paned_window.add(left_frame, weight=1)  # 权重1,默认占一半

        tk.Label(left_frame, text="目录结构 (可视化 - 支持拖拽)", font=("Arial", 10, "bold")).pack(anchor=tk.W,
                                                                                                   pady=(0, 5))

        # 树状图滚动条
        tree_scroll = ttk.Scrollbar(left_frame)
        tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # Treeview 设置
        self.tree = ttk.Treeview(left_frame, selectmode="browse", yscrollcommand=tree_scroll.set)
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        tree_scroll.config(command=self.tree.yview)

        # 绑定拖拽事件
        self.tree.bind("<ButtonPress-1>", self.on_press)
        self.tree.bind("<B1-Motion>", self.on_motion)
        self.tree.bind("<ButtonRelease-1>", self.on_release)

        # 配置 Treeview 列
        self.tree["columns"] = ("type")
        self.tree.column("#0", width=300, anchor=tk.W)
        self.tree.heading("#0", text="名称")
        self.tree.column("type", width=80, anchor=tk.CENTER)
        self.tree.heading("type", text="类型")
        self.tree.tag_configure("folder", foreground="blue", font=("Arial", 10, "bold"))
        self.tree.tag_configure("file", foreground="black")

        # --- 右侧:JSON 代码预览 ---
        right_frame = tk.Frame(self.paned_window)
        self.paned_window.add(right_frame, weight=1)

        # 右侧标题栏
        preview_header = tk.Frame(right_frame)
        preview_header.pack(fill=tk.X, pady=(0, 5))

        tk.Label(preview_header, text="JSON 源码 (可编辑)", font=("Arial", 10, "bold")).pack(side=tk.LEFT)

        # 新增:反向生成按钮
        self.btn_apply = ttk.Button(preview_header, text="← 生成树结构", command=self.apply_json_to_tree)
        self.btn_apply.pack(side=tk.LEFT, padx=10)

        self.btn_copy = ttk.Button(preview_header, text="复制 JSON", command=self.copy_to_clipboard)
        self.btn_copy.pack(side=tk.RIGHT)

        # 文本框滚动条
        text_scroll = ttk.Scrollbar(right_frame)
        text_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # JSON 文本框 (始终允许编辑)
        self.json_text = tk.Text(right_frame, wrap=tk.NONE, yscrollcommand=text_scroll.set, font=("Consolas", 10))
        self.json_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        text_scroll.config(command=self.json_text.yview)

        # 3. 底部控制区 (移动顺序)
        control_frame = tk.Frame(root, pady=10)
        control_frame.pack(fill=tk.X, padx=10)

        tk.Label(control_frame, text="选中项排序:").pack(side=tk.LEFT)
        self.btn_up = ttk.Button(control_frame, text="↑ 上移", command=self.move_up)
        self.btn_up.pack(side=tk.LEFT, padx=5)
        self.btn_down = ttk.Button(control_frame, text="↓ 下移", command=self.move_down)
        self.btn_down.pack(side=tk.LEFT, padx=5)

    def on_press(self, event):
        """鼠标按下:记录准备拖拽的项"""
        item = self.tree.identify_row(event.y)
        if item:
            self.drag_source = item

    def on_motion(self, event):
        """鼠标移动:提供视觉反馈"""
        if not self.drag_source:
            return
        self.tree.config(cursor="fleur")
        target = self.tree.identify_row(event.y)
        if target:
            self.tree.selection_set(target)
            self.tree.focus(target)

    def on_release(self, event):
        """鼠标释放:执行移动操作并更新预览"""
        self.tree.config(cursor="")
        if not self.drag_source:
            return

        target = self.tree.identify_row(event.y)
        if not target or target == self.drag_source:
            self.drag_source = None
            return

        if self.is_descendant(self.drag_source, target):
            messagebox.showwarning("无效操作", "不能将文件夹拖入其自身的子目录中")
            self.drag_source = None
            return

        try:
            target_parent = self.tree.parent(target)
            target_index = self.tree.index(target)
            self.tree.move(self.drag_source, target_parent, target_index)
            # 移动完成后更新预览
            self.update_json_preview()
        except Exception as e:
            print(f"拖拽移动失败: {e}")

        self.drag_source = None

    def is_descendant(self, parent, child):
        """判断 child 是否是 parent 的后代节点"""
        node = child
        while node:
            if node == parent:
                return True
            node = self.tree.parent(node)
        return False

    def select_directory(self):
        """打开文件夹选择框并加载数据"""
        path = filedialog.askdirectory()
        if path:
            self.current_root_path = path
            self.lbl_path.config(text=path, fg="black")
            self.refresh_tree(path)
            self.btn_save.config(state=tk.NORMAL)
            self.update_json_preview()  # 加载完立即显示 JSON

    def refresh_tree(self, path):
        """清空并重新填充树"""
        for item in self.tree.get_children():
            self.tree.delete(item)
        self._insert_node("", path)

    def _insert_node(self, parent_id, current_path):
        """递归扫描目录并插入到 Treeview"""
        try:
            items = os.listdir(current_path)
            items.sort(key=lambda x: (not os.path.isdir(os.path.join(current_path, x)), x.lower()))

            for item in items:
                full_path = os.path.join(current_path, item)
                is_dir = os.path.isdir(full_path)
                if item.startswith('.'):
                    continue

                item_type = "folder" if is_dir else "file"
                oid = self.tree.insert(
                    parent_id,
                    "end",
                    text=item,
                    values=(item_type,),
                    tags=(item_type,),
                    open=False
                )

                if is_dir:
                    self._insert_node(oid, full_path)
        except PermissionError:
            print(f"Permission denied: {current_path}")

    # --- 反向生成逻辑 ---
    def apply_json_to_tree(self):
        """读取右侧 JSON 并反向生成左侧树"""
        content = self.json_text.get(1.0, tk.END).strip()
        if not content:
            messagebox.showwarning("提示", "JSON 内容为空")
            return

        try:
            data = json.loads(content)
        except json.JSONDecodeError as e:
            messagebox.showerror("JSON 解析错误", f"无法解析 JSON:\n{e}")
            return

        # 清空现有树
        for item in self.tree.get_children():
            self.tree.delete(item)

        try:
            self._build_tree_recursive("", data)
            self.lbl_path.config(text="[来自手动输入的 JSON 数据]", fg="blue")
            self.current_root_path = "JSON_SOURCE"  # 标记为非本地路径
            self.btn_save.config(state=tk.NORMAL)
        except Exception as e:
            messagebox.showerror("结构错误", f"构建树失败,请检查 JSON 结构:\n{e}")

    def _build_tree_recursive(self, parent_id, data):
        """递归解析 JSON 列表/字典并插入树"""
        # 如果是列表,遍历处理
        if isinstance(data, list):
            for item in data:
                self._build_tree_recursive(parent_id, item)
            return

        # 如果是字符串 (文件)
        if isinstance(data, str):
            self.tree.insert(parent_id, 'end', text=data, values=('file',), tags=('file',))
            return

        # 如果是字典 (文件夹)
        if isinstance(data, dict):
            name = data.get('name', '未命名')
            item_type = data.get('type', 'folder')
            children = data.get('children', [])

            # 插入文件夹节点
            oid = self.tree.insert(parent_id, 'end', text=name, values=(item_type,), tags=(item_type,), open=True)

            # 递归处理子节点
            self._build_tree_recursive(oid, children)
            return

    # --- 排序与导出 ---

    def move_up(self):
        """将选中的项目向上移动"""
        selected_item = self.tree.selection()
        if not selected_item:
            return
        item = selected_item[0]
        parent = self.tree.parent(item)
        index = self.tree.index(item)
        if index > 0:
            self.tree.move(item, parent, index - 1)
            self.update_json_preview()

    def move_down(self):
        """将选中的项目向下移动"""
        selected_item = self.tree.selection()
        if not selected_item:
            return
        item = selected_item[0]
        parent = self.tree.parent(item)
        current_siblings = self.tree.get_children(parent)
        index = self.tree.index(item)
        if index < len(current_siblings) - 1:
            self.tree.move(item, parent, index + 1)
            self.update_json_preview()

    def update_json_preview(self):
        """生成 JSON 并更新到右侧文本框"""
        data = self._tree_to_json("")
        json_str = json.dumps(data, indent=4, ensure_ascii=False)

        # 更新文本框 (保持可编辑状态)
        self.json_text.delete(1.0, tk.END)
        self.json_text.insert(tk.END, json_str)

    def copy_to_clipboard(self):
        """复制 JSON 内容到剪贴板"""
        content = self.json_text.get(1.0, tk.END)
        self.root.clipboard_clear()
        self.root.clipboard_append(content)
        messagebox.showinfo("提示", "JSON 代码已复制到剪贴板")

    def save_json(self):
        """根据当前 Treeview 的结构生成 JSON"""
        # 注意:这里不再强制检查 self.current_root_path 是否有真实路径
        # 只要树里有东西就可以导出

        file_path = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
            initialfile="structure.json"
        )

        if not file_path:
            return

        data = self._tree_to_json("")

        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=4, ensure_ascii=False)
            messagebox.showinfo("成功", "JSON 文件导出成功!")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")

    def _tree_to_json(self, parent_id):
        """递归将 Treeview 节点转换为列表/字典结构"""
        result_list = []
        children = self.tree.get_children(parent_id)

        for child_id in children:
            item_data = self.tree.item(child_id)
            name = item_data['text']
            # 安全获取 values
            values = item_data.get('values', [])
            item_type = values[0] if values else 'file'

            if item_type == 'file':
                result_list.append(name)
            elif item_type == 'folder':
                folder_obj = {
                    "name": name,
                    "type": "folder",
                    "children": self._tree_to_json(child_id)
                }
                result_list.append(folder_obj)

        return result_list


if __name__ == "__main__":
    root = tk.Tk()
    try:
        from ctypes import windll

        windll.shcore.SetProcessDpiAwareness(1)
    except:
        pass

    app = DirectoryMapperApp(root)
    root.mainloop()