文件树提取脚本2.0(增加了对matlab script的支持)

import os
import ast
import re
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext

# 定义一个函数来提取Python文件中的主要框架
def extract_python_framework(file_path):
    # def extract_python_framework(file_path):
    # 定义名为 extract_python_framework 的函数,接收一个参数 file_path(文件路径)
    try:
        # try:
        # 开始一个异常处理块
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # 以只读模式('r')打开指定路径的文件,使用 utf-8 编码,并忽略解码错误
            # 将打开的文件对象赋值给变量 file,with 语句确保文件最终会被关闭
            file_content = file.read()
            # file_content = file.read():
            # 读取文件的全部内容并存储在变量 file_content 中

        tree = ast.parse(file_content)
        # tree = ast.parse(file_content):
        # 使用 ast (Abstract Syntax Tree) 模块的 parse 方法将文件内容解析成一个抽象语法树
        framework = []
        # framework = []:
        # 初始化一个空列表 framework,用于存储提取到的框架信息

        for node in ast.walk(tree):
            # for node in ast.walk(tree):
            # 遍历抽象语法树中的所有节点
            if isinstance(node, ast.FunctionDef):
                # if isinstance(node, ast.FunctionDef):
                # 如果当前节点是函数定义(ast.FunctionDef 类的实例)
                framework.append(f'函数: {node.name}')
                # framework.append(f'函数: {node.name}'):
                # 将格式化后的函数信息(例如 "函数: my_function")添加到 framework 列表中
            elif isinstance(node, ast.ClassDef):
                # elif isinstance(node, ast.ClassDef):
                # 如果当前节点是类定义(ast.ClassDef 类的实例)
                framework.append(f'类: {node.name}')
                # framework.append(f'类: {node.name}'):
                # 将格式化后的类信息(例如 "类: MyClass")添加到 framework 列表中

        return framework
        # return framework:
        # 返回包含提取到的框架信息的列表
    except SyntaxError:
        # except SyntaxError:
        # 如果在解析文件时发生语法错误 (SyntaxError)
        print(f'警告: 文件 {file_path} 包含语法错误,已跳过。')
        # print(f'警告: 文件 {file_path} 包含语法错误,已跳过。'):
        # 打印警告信息,指出文件因语法错误而被跳过
        return []
        # return []:
        # 返回一个空列表
    except Exception as e:
        # except Exception as e:
        # 如果发生其他任何类型的异常
        print(f'错误: 文件 {file_path} 处理时发生异常: {e}')
        # print(f'错误: 文件 {file_path} 处理时发生异常: {e}'):
        # 打印错误信息,包括文件名和异常的具体内容
        return []
        # return []:
        # 返回一个空列表

# 定义一个函数来提取C++文件中的主要框架
def extract_cpp_framework(file_path):
    # def extract_cpp_framework(file_path):
    # 定义名为 extract_cpp_framework 的函数,接收一个参数 file_path(文件路径)
    try:
        # try:
        # 开始一个异常处理块
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # 以只读模式打开文件,使用 utf-8 编码,忽略错误
            file_content = file.read()
            # file_content = file.read():
            # 读取文件内容

        # 使用正则表达式提取函数和类定义
        function_pattern = re.compile(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{')
        # function_pattern = re.compile(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{'):
        # 编译一个正则表达式用于匹配 C++ 函数定义。
        # \b\w+\s+\w+\s*: 匹配单词边界、一个或多个单词字符(返回类型)、一个或多个空格、一个或多个单词字符(函数名)、可选空格
        # \([^)]*\): 匹配括号及其内部的任何字符(参数列表)
        # \s*\{: 匹配可选空格和左花括号
        class_pattern = re.compile(r'class\s+\w+\s*\{')
        # class_pattern = re.compile(r'class\s+\w+\s*\{'):
        # 编译一个正则表达式用于匹配 C++ 类定义。
        # class\s+\w+: 匹配 "class" 关键字、一个或多个空格、一个或多个单词字符(类名)
        # \s*\{: 匹配可选空格和左花括号

        functions = function_pattern.findall(file_content)
        # functions = function_pattern.findall(file_content):
        # 在文件内容中查找所有匹配函数模式的字符串,并返回一个列表
        classes = class_pattern.findall(file_content)
        # classes = class_pattern.findall(file_content):
        # 在文件内容中查找所有匹配类模式的字符串,并返回一个列表

        framework = []
        # framework = []:
        # 初始化一个空列表 framework 用于存储提取到的框架信息
        for func in functions:
            # for func in functions:
            # 遍历找到的每个函数匹配项
            framework.append(f'函数: {func.split("(")[0].strip()}')
            # framework.append(f'函数: {func.split("(")[0].strip()}'):
            # 将匹配到的函数字符串按 "(" 分割,取第一部分(包含返回类型和函数名),去除首尾空格,格式化后添加到 framework 列表
        for cls in classes:
            # for cls in classes:
            # 遍历找到的每个类匹配项
            framework.append(f'类: {cls.split("{")[0].strip()}')
            # framework.append(f'类: {cls.split("{")[0].strip()}'):
            # 将匹配到的类字符串按 "{" 分割,取第一部分(包含 "class" 和类名),去除首尾空格,格式化后添加到 framework 列表

        return framework
        # return framework:
        # 返回提取到的框架信息列表
    except Exception as e:
        # except Exception as e:
        # 如果在处理文件时发生任何异常
        print(f'错误: 文件 {file_path} 处理时发生异常: {e}')
        # print(f'错误: 文件 {file_path} 处理时发生异常: {e}'):
        # 打印错误信息
        return []
        # return []:
        # 返回空列表

# 定义一个函数来提取MATLAB文件中的主要框架
def extract_matlab_framework(file_path):
    # def extract_matlab_framework(file_path):
    # 定义名为 extract_matlab_framework 的函数,接收文件路径作为参数
    try:
        # try:
        # 开始异常捕获
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
            # 以只读('r')模式打开文件,使用 'utf-8' 编码,忽略解码错误
            file_content = file.read()
            # file_content = file.read():
            # 读取文件所有内容到 file_content 变量

        # 正则表达式提取 MATLAB 函数: function (output) = name(input) 或 function name(input)
        # (?:...) 是非捕获组
        # \s* 表示任意数量的空白字符
        # \w+ 表示一个或多个字母数字字符(用于名称)
        # \[.*?\] 匹配方括号内的任何字符(用于输出参数)
        # \(.*?\) 匹配圆括号内的任何字符(用于输入参数)
        # ^ 表示行首,m 表示多行模式
        function_pattern = re.compile(r'^\s*function(?:\s+\[.*?\])?\s*=?\s*(\w+)\s*\(.*?\)', re.MULTILINE)
        # function_pattern = re.compile(r'^\s*function(?:\s+\[.*?\])?\s*=?\s*(\w+)\s*\(.*?\)', re.MULTILINE):
        # 编译一个正则表达式来匹配 MATLAB 函数定义。
        # ^\s*function: 匹配行首(考虑多行模式 re.MULTILINE)、可选空格和 "function" 关键字。
        # (?:\s+\[.*?\])?\s*=: 这是一个可选的非捕获组,匹配可选的输出参数(如 `[out1, out2]`)和等号。
        #   \s+\[.*?\]: 匹配空格和方括号括起来的任何内容(输出参数)。
        #   \s*=\s*: 匹配等号,前后可能有空格。
        # (\w+): 捕获组,匹配函数名(一个或多个单词字符)。
        # \s*\(.*?\): 匹配可选空格、圆括号以及括号内的任何字符(输入参数)。
        # re.MULTILINE: 使 `^` 匹配每一行的开始,而不仅仅是字符串的开始。

        # 正则表达式提取 MATLAB 类: classdef ClassName
        class_pattern = re.compile(r'^\s*classdef\s+(\w+)', re.MULTILINE)
        # class_pattern = re.compile(r'^\s*classdef\s+(\w+)', re.MULTILINE):
        # 编译一个正则表达式来匹配 MATLAB 类定义。
        # ^\s*classdef: 匹配行首(考虑多行模式)、可选空格和 "classdef" 关键字。
        # \s+(\w+): 匹配一个或多个空格,然后捕获类名(一个或多个单词字符)。

        functions = function_pattern.findall(file_content)
        # functions = function_pattern.findall(file_content):
        # 在文件内容中查找所有匹配函数模式的函数名,并返回一个包含这些名称的列表
        classes = class_pattern.findall(file_content)
        # classes = class_pattern.findall(file_content):
        # 在文件内容中查找所有匹配类模式的类名,并返回一个包含这些名称的列表

        framework = []
        # framework = []:
        # 初始化一个空列表 framework 用于存储提取的框架信息
        for func_name in functions:
            # for func_name in functions:
            # 遍历所有找到的函数名
            framework.append(f'函数: {func_name.strip()}')
            # framework.append(f'函数: {func_name.strip()}'):
            # 将格式化后的函数信息(例如 "函数: myFunction")添加到 framework 列表中,strip()确保去除可能存在的多余空格
        for class_name in classes:
            # for class_name in classes:
            # 遍历所有找到的类名
            framework.append(f'类: {class_name.strip()}')
            # framework.append(f'类: {class_name.strip()}'):
            # 将格式化后的类信息(例如 "类: MyClass")添加到 framework 列表中

        return framework
        # return framework:
        # 返回包含提取框架信息的列表
    except Exception as e:
        # except Exception as e:
        # 如果在处理文件时发生任何异常
        print(f'错误: 文件 {file_path} 处理MATLAB文件时发生异常: {e}')
        # print(f'错误: 文件 {file_path} 处理MATLAB文件时发生异常: {e}'):
        # 打印错误信息,指明是处理MATLAB文件时发生的错误
        return []
        # return []:
        # 返回空列表

# 定义一个函数来遍历文件夹的文件树
def extract_folder_framework(folder_path):
    # def extract_folder_framework(folder_path):
    # 定义名为 extract_folder_framework 的函数,接收一个参数 folder_path(文件夹路径)
    frameworks = {}
    # frameworks = {}:
    # 初始化一个空字典 frameworks,用于存储每个文件的框架信息,键为文件路径,值为框架列表

    for root, dirs, files in os.walk(folder_path):
        # for root, dirs, files in os.walk(folder_path):
        # 使用 os.walk 遍历指定文件夹及其所有子文件夹
        # root: 当前正在遍历的文件夹路径
        # dirs: root 文件夹中的子文件夹列表
        # files: root 文件夹中的文件列表
        for file in files:
            # for file in files:
            # 遍历当前文件夹中的每个文件
            file_path = os.path.join(root, file)
            # file_path = os.path.join(root, file):
            # 构建文件的完整路径
            if file.endswith('.py'):
                # if file.endswith('.py'):
                # 如果文件名以 '.py' 结尾(Python 文件)
                framework = extract_python_framework(file_path)
                # framework = extract_python_framework(file_path):
                # 调用 extract_python_framework 函数提取框架
                frameworks[file_path] = framework
                # frameworks[file_path] = framework:
                # 将提取到的框架信息存入 frameworks 字典
            elif file.endswith('.cpp') or file.endswith('.h'):
                # elif file.endswith('.cpp') or file.endswith('.h'):
                # 如果文件名以 '.cpp' 或 '.h' 结尾(C++ 源文件或头文件)
                framework = extract_cpp_framework(file_path)
                # framework = extract_cpp_framework(file_path):
                # 调用 extract_cpp_framework 函数提取框架
                frameworks[file_path] = framework
                # frameworks[file_path] = framework:
                # 将提取到的框架信息存入 frameworks 字典
            elif file.endswith('.m'): # 新增对 .m 文件的处理
                # elif file.endswith('.m'):
                # 如果文件名以 '.m' 结尾(MATLAB 文件)
                framework = extract_matlab_framework(file_path)
                # framework = extract_matlab_framework(file_path):
                # 调用新增的 extract_matlab_framework 函数提取框架
                frameworks[file_path] = framework
                # frameworks[file_path] = framework:
                # 将提取到的框架信息存入 frameworks 字典
            else:
                # else:
                # 对于其他类型的文件
                frameworks[file_path] = [f'文件名: {file}']
                # frameworks[file_path] = [f'文件名: {file}']:
                # 将文件名作为信息存入 frameworks 字典(表示未进行深度解析)

    return frameworks
    # return frameworks:
    # 返回包含所有文件框架信息的字典

# 定义一个函数来处理文件夹选择
def select_folder():
    # def select_folder():
    # 定义名为 select_folder 的函数,用于弹出文件夹选择对话框
    folder_path = filedialog.askdirectory()
    # folder_path = filedialog.askdirectory():
    # 打开一个标准的文件夹选择对话框,并返回用户选择的文件夹路径
    if folder_path:
        # if folder_path:
        # 如果用户选择了文件夹(路径不为空)
        entry_folder.delete(0, tk.END)
        # entry_folder.delete(0, tk.END):
        # 清空文件夹路径输入框(entry_folder)中的当前内容
        entry_folder.insert(0, folder_path)
        # entry_folder.insert(0, folder_path):
        # 将用户选择的文件夹路径插入到输入框的开头

# 定义一个函数来执行提取并显示结果
def run_extraction():
    # def run_extraction():
    # 定义名为 run_extraction 的函数,用于执行框架提取并显示结果
    folder_path = entry_folder.get()
    # folder_path = entry_folder.get():
    # 获取当前文件夹路径输入框中的文本内容
    if not folder_path:
        # if not folder_path:
        # 如果文件夹路径为空
        messagebox.showwarning("警告", "请先选择文件夹!")
        # messagebox.showwarning("警告", "请先选择文件夹!"):
        # 显示一个警告消息框,提示用户先选择文件夹
        return
        # return:
        # 提前结束函数执行

    frameworks = extract_folder_framework(folder_path)
    # frameworks = extract_folder_framework(folder_path):
    # 调用 extract_folder_framework 函数提取所选文件夹中所有适用文件的框架

    text_result.delete(1.0, tk.END)
    # text_result.delete(1.0, tk.END):
    # 清空结果显示文本框(text_result)中的所有内容(从第1行第0列到末尾)

    if not frameworks:
        # if not frameworks:
        # 如果提取结果为空(没有找到可解析的文件或提取内容为空)
        text_result.insert(tk.END, "未找到任何可解析的文件。\n")
        # text_result.insert(tk.END, "未找到任何可解析的文件。\n"):
        # 在结果文本框中插入提示信息
    else:
        # else:
        # 如果提取到了框架信息
        for file_path, framework in frameworks.items():
            # for file_path, framework in frameworks.items():
            # 遍历提取结果字典中的每一项(文件路径和对应的框架列表)
            text_result.insert(tk.END, f'文件: {file_path}\n')
            # text_result.insert(tk.END, f'文件: {file_path}\n'):
            # 在结果文本框中插入文件名
            if framework: # 确保framework不是空的,例如,如果matlab文件没有函数或类
                # if framework:
                # 如果该文件的框架列表不为空
                for item in framework:
                    # for item in framework:
                    # 遍历框架列表中的每一项(如 "函数: xxx" 或 "类: yyy")
                    text_result.insert(tk.END, f'  {item}\n')
                    # text_result.insert(tk.END, f'  {item}\n'):
                    # 在结果文本框中插入框架项,并添加缩进
            else: # 如果 framework 为空(例如,.m 文件但没有找到函数或类定义)
                # else:
                # 如果该文件的框架列表为空
                text_result.insert(tk.END, f'  (无特定框架结构被提取)\n')
                # text_result.insert(tk.END, f'  (无特定框架结构被提取)\n'):
                # 插入一条提示信息,表明未提取到特定结构
            text_result.insert(tk.END, '\n')
            # text_result.insert(tk.END, '\n'):
            # 在每个文件信息后插入一个空行,用于分隔

    messagebox.showinfo("完成", "框架提取完成!")
    # messagebox.showinfo("完成", "框架提取完成!"):
    # 显示一个信息消息框,提示框架提取已完成

# 定义一个函数来导出结果到TXT文件
def export_to_txt():
    # def export_to_txt():
    # 定义名为 export_to_txt 的函数,用于将提取结果导出到 TXT 文件
    folder_path = entry_folder.get()
    # folder_path = entry_folder.get():
    # 获取文件夹路径输入框的内容
    if not folder_path:
        # if not folder_path:
        # 如果路径为空
        messagebox.showwarning("警告", "请先选择文件夹!")
        # messagebox.showwarning("警告", "请先选择文件夹!"):
        # 显示警告
        return
        # return:
        # 提前返回

    frameworks = extract_folder_framework(folder_path)
    # frameworks = extract_folder_framework(folder_path):
    # 再次执行框架提取,以获取最新的数据(或者可以考虑缓存上次结果)

    save_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("文本文件", "*.txt")])
    # save_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("文本文件", "*.txt")]):
    # 打开一个 "另存为" 对话框,让用户选择保存文件的路径和名称
    # defaultextension=".txt": 默认文件扩展名为 .txt
    # filetypes=[("文本文件", "*.txt")]: 文件类型过滤器,只显示 .txt 文件
    if not save_path:
        # if not save_path:
        # 如果用户取消了保存操作(路径为空)
        return
        # return:
        # 提前返回

    with open(save_path, 'w', encoding='utf-8') as file:
        # with open(save_path, 'w', encoding='utf-8') as file:
        # 以写入模式('w')打开用户选择的文件路径,使用 utf-8 编码
        if not frameworks:
            # if not frameworks:
            # 如果没有提取到框架信息
            file.write("未找到任何可解析的文件。\n")
            # file.write("未找到任何可解析的文件。\n"):
            # 写入提示信息
        else:
            # else:
            # 如果有框架信息
            for file_path, framework in frameworks.items():
                # for file_path, framework in frameworks.items():
                # 遍历提取结果
                file.write(f'文件: {file_path}\n')
                # file.write(f'文件: {file_path}\n'):
                # 写入文件名
                if framework:
                    # if framework:
                    # 如果框架列表不为空
                    for item in framework:
                        # for item in framework:
                        # 遍历框架项
                        file.write(f'  {item}\n')
                        # file.write(f'  {item}\n'):
                        # 写入框架项
                else:
                    # else:
                    # 如果框架列表为空
                    file.write(f'  (无特定框架结构被提取)\n')
                    # file.write(f'  (无特定框架结构被提取)\n'):
                    # 写入提示信息
                file.write('\n')
                # file.write('\n'):
                # 写入空行分隔

    messagebox.showinfo("完成", f"结果已导出到 {save_path}!")
    # messagebox.showinfo("完成", f"结果已导出到 {save_path}!"):
    # 显示导出完成的信息,并告知保存路径

# 创建主窗口
root = tk.Tk()
# root = tk.Tk():
# 创建 Tkinter 的根窗口对象
root.title("脚本框架提取工具")
# root.title("脚本框架提取工具"):
# 设置窗口的标题
root.geometry("800x600")
# root.geometry("800x600"):
# 设置窗口的初始大小为 800x600 像素

# 创建文件夹选择部分
frame_folder = tk.Frame(root)
# frame_folder = tk.Frame(root):
# 创建一个 Frame 控件作为容器,父控件为根窗口 root
frame_folder.pack(pady=10)
# frame_folder.pack(pady=10):
# 将 frame_folder 放置到父控件中,并在垂直方向上添加 10 像素的外边距

label_folder = tk.Label(frame_folder, text="选择文件夹:")
# label_folder = tk.Label(frame_folder, text="选择文件夹:"):
# 创建一个 Label 控件,显示文本 "选择文件夹:",父控件为 frame_folder
label_folder.pack(side=tk.LEFT)
# label_folder.pack(side=tk.LEFT):
# 将 label_folder 放置到其父控件的左侧

entry_folder = tk.Entry(frame_folder, width=50)
# entry_folder = tk.Entry(frame_folder, width=50):
# 创建一个 Entry 控件(文本输入框),宽度为 50 个字符,父控件为 frame_folder
entry_folder.pack(side=tk.LEFT, padx=5)
# entry_folder.pack(side=tk.LEFT, padx=5):
# 将 entry_folder 放置到其父控件的左侧,并在水平方向上添加 5 像素的外边距

button_select = tk.Button(frame_folder, text="浏览", command=select_folder)
# button_select = tk.Button(frame_folder, text="浏览", command=select_folder):
# 创建一个 Button 控件,显示文本 "浏览",点击时执行 select_folder 函数,父控件为 frame_folder
button_select.pack(side=tk.LEFT)
# button_select.pack(side=tk.LEFT):
# 将 button_select 放置到其父控件的左侧

# 创建结果展示部分
text_result = scrolledtext.ScrolledText(root, width=100, height=30)
# text_result = scrolledtext.ScrolledText(root, width=100, height=30):
# 创建一个 ScrolledText 控件(带滚动条的文本区域),宽度为 100 字符,高度为 30 行,父控件为根窗口 root
text_result.pack(pady=10)
# text_result.pack(pady=10):
# 将 text_result 放置到父控件中,并在垂直方向上添加 10 像素的外边距

# 创建按钮部分
frame_buttons = tk.Frame(root)
# frame_buttons = tk.Frame(root):
# 创建另一个 Frame 控件作为按钮的容器,父控件为根窗口 root
frame_buttons.pack(pady=10)
# frame_buttons.pack(pady=10):
# 将 frame_buttons 放置到父控件中,并在垂直方向上添加 10 像素的外边距

button_run = tk.Button(frame_buttons, text="提取框架", command=run_extraction)
# button_run = tk.Button(frame_buttons, text="提取框架", command=run_extraction):
# 创建一个 Button 控件,显示文本 "提取框架",点击时执行 run_extraction 函数,父控件为 frame_buttons
button_run.pack(side=tk.LEFT, padx=5)
# button_run.pack(side=tk.LEFT, padx=5):
# 将 button_run 放置到其父控件的左侧,并在水平方向上添加 5 像素的外边距

button_export = tk.Button(frame_buttons, text="导出为TXT", command=export_to_txt)
# button_export = tk.Button(frame_buttons, text="导出为TXT", command=export_to_txt):
# 创建一个 Button 控件,显示文本 "导出为TXT",点击时执行 export_to_txt 函数,父控件为 frame_buttons
button_export.pack(side=tk.LEFT, padx=5)
# button_export.pack(side=tk.LEFT, padx=5):
# 将 button_export 放置到其父控件的左侧,并在水平方向上添加 5 像素的外边距

# 运行主循环
root.mainloop()
# root.mainloop():
# 启动 Tkinter 的事件循环,使窗口能够响应用户操作和事件,直到窗口关闭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值