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 的事件循环,使窗口能够响应用户操作和事件,直到窗口关闭
文件树提取脚本2.0(增加了对matlab script的支持)
最新推荐文章于 2025-05-22 19:36:31 发布