解析 Java 项目生成常量、变量和函数 Excel 文档

news2024/12/27 7:24:10

解析 Java 项目生成常量、变量和函数 Excel 文档

  • 一、需求描述
  • 二、实现步骤
  • 三、实现代码

一、需求描述

已知 Java 项目源代码,需要输出一份程序模块设计说明书,基于模块的设计考虑需要包括程序描述、输入/输出、算法和流程逻辑等,为软件编程和系统维护提供基础。由于工程量较大,希望通过快速简易的方式自动生成。

需求转化:根据 Java 源代码输出一份 Excel 表格汇总如下信息

常量(文件名称 常量名称 常量类型 常量说明)
变量(文件名称 变量 变量类型 功能说明)
函数(文件名称 函数 功能 格式 参数 全局变量 局部变量 返回值)

二、实现步骤

  1. 输入:Java 项目路径和输出文件路径

  2. 扫描:递归寻找所有 .java 文件

  3. 解析每个文件:

    • 匹配并提取常量
    • 匹配并提取变量
    • 匹配并提取方法
  4. 整合所有文件的解析结果

  5. 导出为 Excel 表格

三、实现代码

这里主要使用 Python 的正则匹配完成常量、变量和函数的提取,只能完成80%的要求,还存在问题需要手动修正

import os
import re
import pandas as pd

# 定义正则表达式
CONST_PATTERN = r"(public|protected|private)?\s+static\s+final\s+(\w+)\s+(\w+)\s*=\s*.+;"  # 常量
VAR_PATTERN = r"(private|protected|public)?\s*(static)?\s*(final)?\s*(\w+)\s+(\w+)\s*;"  # 变量
METHOD_PATTERN = r"(public|protected|private)?\s+(\w+)\s+(\w+)\s*\((.*?)\)\s*\{"  # 方法

# 定义扫描目录的函数
def scan_java_files(directory):
    java_files = []
    for root, _, files in os.walk(directory):
        for file in files:
            if file.endswith(".java"):
                java_files.append(os.path.join(root, file))
    return java_files

# 解析 Java 文件
def parse_java_file(file_path):
    constants = []
    variables = []
    methods = []

    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

        # 提取常量
        for match in re.finditer(CONST_PATTERN, content):
            access_modifier, const_type, const_name = match.groups()[:3]
            constants.append({
                "文件名称": os.path.basename(file_path),
                "所在目录": os.path.dirname(file_path),
                "常量名称": const_name,
                "常量类型": const_type,
                "功能说明": "常量描述待补充"
            })

        # 提取变量
        for match in re.finditer(VAR_PATTERN, content):
            access_modifier, is_static, is_final, var_type, var_name = match.groups()
            variables.append({
                "文件名称": os.path.basename(file_path),
                "变量": var_name,
                "变量类型": var_type,
                "功能说明": "变量描述待补充"
            })

        # 提取方法
        for match in re.finditer(METHOD_PATTERN, content):
            access_modifier, return_type, method_name, params = match.groups()
            methods.append({
                "文件名称": os.path.basename(file_path),
                "函数": method_name,
                "功能": "函数描述待补充",
                "格式": f"{return_type} {method_name}({params})",
                "参数": params,
                "全局变量": "待补充",
                "局部变量": "待补充",
                "返回值": return_type
            })

    return constants, variables, methods

# 主函数
def main(java_project_dir, output_excel_file):
    java_files = scan_java_files(java_project_dir)
    all_constants = []
    all_variables = []
    all_methods = []

    for java_file in java_files:
        constants, variables, methods = parse_java_file(java_file)
        all_constants.extend(constants)
        all_variables.extend(variables)
        all_methods.extend(methods)

    # 保存为 Excel 文件
    with pd.ExcelWriter(output_excel_file) as writer:
        pd.DataFrame(all_constants).to_excel(writer, sheet_name="常量", index=False)
        pd.DataFrame(all_variables).to_excel(writer, sheet_name="变量", index=False)
        pd.DataFrame(all_methods).to_excel(writer, sheet_name="函数", index=False)

    print(f"解析完成,结果已保存到 {output_excel_file}")

# 执行脚本
if __name__ == "__main__":
    java_project_dir = "/home/fangjian/code/Cremb"  # 替换为你的 Java 项目目录路径
    output_excel_file = "java_project_analysis.xlsx"  # 输出文件名
    main(java_project_dir, output_excel_file)

问题点

  1. 只能匹配 Java 基础类型,无法匹配自定义类型,如MediaType、List
  2. 函数方法匹配时出现很多 if-else,解析不正确

优化方案

  1. 改进正则表达式以支持复杂类型

    • 修改了类型匹配模式,使用 [<>?\w\s,]+ 支持泛型和自定义类型
    • 增加了对范型参数的处理
  2. 增加了新的辅助函数:

    • remove_comments_and_strings(): 预处理代码内容
    • is_valid_java_type(): 验证Java类型
    • clean_name(): 清理和验证标识符

优化后还是会存在一些问题,需要后期人工审查时自行调整,欢迎大家给出更好的信息匹配方案,或者其他类似的开源项目。

import os
import re
import pandas as pd

# 改进的正则表达式模式
CONST_PATTERN = r"(?:public|protected|private)?\s+static\s+final\s+([<>?\w\s,]+)\s+(\w+)\s*=\s*.+;"

# 改进的变量模式
VAR_PATTERN = r"(?:private|protected|public)?\s*(?:static)?\s*(?:final)?\s*([<>?\w\s,]+)\s+(\w+)\s*(?:=\s*[^;]+)?;"

# 改进的方法模式,更准确的匹配
METHOD_PATTERN = r"(?:public|protected|private)?\s*(?:static)?\s*(?:abstract\s+)?([<>?\w\s,]+)\s+(\w+)\s*\(([\s\S]*?)\)\s*(?:throws\s+[\w\s,]+\s*)?(?=\{|;)"

# 排除关键字
EXCLUDE_KEYWORDS = {
    'if', 'else', 'for', 'while', 'switch', 'catch', 'finally', 'synchronized',
    'return', 'break', 'continue', 'throw', 'assert', 'case', 'default'
}

# 有效的Java类型集合
VALID_JAVA_TYPES = {
    'void', 'boolean', 'byte', 'char', 'short', 'int', 'long', 'float', 'double',
    'String', 'Object', 'Boolean', 'Byte', 'Character', 'Short', 'Integer', 'Long',
    'Float', 'Double', 'List', 'Map', 'Set', 'Collection', 'ArrayList', 'HashMap',
    'HashSet', 'Vector', 'LinkedList', 'TreeMap', 'TreeSet', 'StringBuilder',
    'StringBuffer', 'View', 'ViewGroup', 'Context', 'Activity', 'Fragment',
    'Intent', 'Bundle', 'Cursor', 'Handler', 'Thread', 'Runnable', 'Exception',
    'RuntimeException', 'Button', 'TextView', 'EditText', 'ImageView', 'LinearLayout',
    'RelativeLayout', 'FrameLayout', 'RecyclerView', 'ListView', 'GridView'
}


def clean_type(type_str):
    """清理并验证类型字符串"""
    if not type_str:
        return ""
    cleaned = ' '.join(type_str.split())
    base_type = re.sub(r'<.*>', '', cleaned).split()[0]
    if base_type in EXCLUDE_KEYWORDS:
        return ""
    return cleaned


def clean_name(name):
    """清理并验证名称"""
    if not name:
        return ""
    name = name.strip()
    if not re.match(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$', name) or name in EXCLUDE_KEYWORDS:
        return ""
    return name


def is_valid_java_type(type_str):
    """验证是否是有效的Java类型"""
    if not type_str:
        return False

    # 清理类型字符串
    base_type = re.sub(r'<.*>', '', type_str.strip()).split()[0]

    # 检查是否是已知的Java类型
    if base_type in VALID_JAVA_TYPES:
        return True

    # 检查是否是自定义类型(首字母大写)
    if re.match(r'^[A-Z][a-zA-Z0-9_$]*$', base_type):
        return True

    return False


def parse_parameters(params_str):
    """解析方法参数"""
    if not params_str or params_str.isspace():
        return ""

    params = []
    current_param = []
    bracket_count = 0

    for char in params_str:
        if char == '<':
            bracket_count += 1
        elif char == '>':
            bracket_count -= 1
        elif char == ',' and bracket_count == 0:
            param = ''.join(current_param).strip()
            if param:
                params.append(param)
            current_param = []
            continue
        current_param.append(char)

    if current_param:
        param = ''.join(current_param).strip()
        if param:
            params.append(param)

    valid_params = []
    for param in params:
        param_parts = param.split()
        if len(param_parts) >= 2:
            param_type = param_parts[0]
            if is_valid_java_type(param_type):
                valid_params.append(param)

    return ', '.join(valid_params)


def remove_comments_and_strings(content):
    """移除注释、字符串字面量和注解"""
    # 移除多行注释
    content = re.sub(r'/\*[\s\S]*?\*/', '', content)
    # 移除单行注释
    content = re.sub(r'//.*', '', content)
    # 移除字符串字面量
    content = re.sub(r'"(?:\\.|[^"\\])*"', '""', content)
    # 移除注解
    content = re.sub(r'@\w+(?:\([^)]*\))?', '', content)
    return content


def extract_method_info(match, return_type):
    """提取方法信息"""
    try:
        method_name = match.group(2)
        params = match.group(3)

        # 验证方法名
        method_name = clean_name(method_name)
        if not method_name or method_name in EXCLUDE_KEYWORDS:
            return None

        # 验证返回类型
        return_type = clean_type(return_type)
        if not return_type and return_type != 'void':
            return None

        return {
            "函数": method_name,
            "功能": "函数描述待补充",
            "格式": f"{return_type} {method_name}({parse_parameters(params)})",
            "参数": parse_parameters(params),
            "全局变量": "待补充",
            "局部变量": "待补充",
            "返回值": return_type
        }
    except Exception:
        return None


def parse_java_file(file_path):
    constants = []
    variables = []
    methods = []

    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        # 预处理内容
        content = remove_comments_and_strings(content)

        # 提取常量
        for match in re.finditer(CONST_PATTERN, content):
            try:
                const_type, const_name = match.groups()
                const_type = clean_type(const_type)
                const_name = clean_name(const_name)

                if const_type and const_name and is_valid_java_type(const_type):
                    constants.append({
                        "文件名称": os.path.basename(file_path),
                        "所在目录": os.path.dirname(file_path),
                        "常量名称": const_name,
                        "常量类型": const_type,
                        "功能说明": "常量描述待补充"
                    })
            except Exception:
                continue

        # 提取变量
        for match in re.finditer(VAR_PATTERN, content):
            try:
                var_type, var_name = match.groups()
                var_type = clean_type(var_type)
                var_name = clean_name(var_name)

                if var_type and var_name and is_valid_java_type(var_type):
                    variables.append({
                        "文件名称": os.path.basename(file_path),
                        "变量": var_name,
                        "变量类型": var_type,
                        "功能说明": "变量描述待补充"
                    })
            except Exception:
                continue

        # 提取方法
        for match in re.finditer(METHOD_PATTERN, content):
            try:
                return_type = match.group(1)
                method_info = extract_method_info(match, return_type)

                if method_info:
                    method_info["文件名称"] = os.path.basename(file_path)
                    methods.append(method_info)
            except Exception:
                continue

    except Exception as e:
        print(f"处理文件 {file_path} 时出错: {str(e)}")

    return constants, variables, methods


def main(java_project_dir, output_excel_file):
    java_files = []
    all_constants = []
    all_variables = []
    all_methods = []

    # 扫描Java文件
    try:
        for root, _, files in os.walk(java_project_dir):
            for file in files:
                if file.endswith(".java"):
                    java_files.append(os.path.join(root, file))
    except Exception as e:
        print(f"扫描目录时出错: {str(e)}")
        return

    # 处理每个文件
    for java_file in java_files:
        try:
            constants, variables, methods = parse_java_file(java_file)
            all_constants.extend(constants)
            all_variables.extend(variables)
            all_methods.extend(methods)
        except Exception as e:
            print(f"处理文件 {java_file} 时出错: {str(e)}")
            continue

    # 保存结果
    try:
        with pd.ExcelWriter(output_excel_file) as writer:
            pd.DataFrame(all_constants).to_excel(writer, sheet_name="常量", index=False)
            pd.DataFrame(all_variables).to_excel(writer, sheet_name="变量", index=False)
            pd.DataFrame(all_methods).to_excel(writer, sheet_name="函数", index=False)
        print(f"解析完成,结果已保存到 {output_excel_file}")
    except Exception as e:
        print(f"保存Excel文件时出错: {str(e)}")


if __name__ == "__main__":
    java_project_dir = "/home/fangjian/Cremb"
    output_excel_file = "java_project_analysis_note.xlsx"
    main(java_project_dir, output_excel_file)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

能省一点是一点 - 享元模式(Flyweight Pattern)

享元模式&#xff08;Flyweight Pattern&#xff09; 享元模式&#xff08;Flyweight Pattern&#xff09;享元模式&#xff08;Flyweight Pattern&#xff09;概述享元模式包含的角色&#xff1a;享元模式应用场景 talk is cheap&#xff0c; show you my code总结 享元模式&a…

学习C++:变量

变量&#xff1a; 作用&#xff1a;给一段指定的内存空间起名&#xff0c;方便操作这段内容 &#xff08;变量存在的意义&#xff1a;方便我们管理内存空间&#xff09; 语法&#xff1a;数据类型 变量名 初始值&#xff1b; 实例&#xff1a;

蓝桥杯物联网开发板硬件组成

第一节 开发板简介 物联网设计与开发竞赛实训平台由蓝桥杯大赛技术支持单位北京四梯科技有限公司设计和生产&#xff0c;该产品可用于参加蓝桥杯物联网设计与开发赛道的竞赛实训或院校相关课程的 实践教学环节。 开发板基于STM32WLE5无线微控制器设计&#xff0c;芯片提供了25…

Day35汉明距离

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y&#xff0c;计算并返回它们之间的汉明距离。 class Solution {public int hammingDistance(int x, int y) {int cnt 0;while (Math.max(x, y) ! 0) {if ((x & 1) ! (y &…

matplotlib pyton 如何画柱状图,利用kimi,直接把图拉倒上面,让他生成

要绘制类似于您提供的图像的柱状图&#xff0c;您可以使用Python中的Matplotlib库&#xff0c;这是一个非常流行的绘图库。以下是一个简单的示例代码&#xff0c;展示如何使用Matplotlib来创建一个类似的柱状图&#xff1a; python import matplotlib.pyplot as plt import nu…

计算机网络——期末复习(3)4-6章考试重点

第四章 根据IPv4第1个十进制数值判断&#xff0c;127以下为A类&#xff0c;128~191为B类&#xff0c;192~223为C类不能分配给主机或路由器接口的&#xff1a;A类网络号0和127&#xff0c;主机号全为0或全为1私有地址&#xff08;Private IP Address&#xff09;是指一类专门保…

【安全编码】Web平台如何设计防止重放攻击

我们先来做一道关于防重放的题&#xff0c;答案在文末 防止重放攻击最有效的方法是&#xff08; &#xff09;。 A.对用户密码进行加密存储使用 B.使用一次一密的加密方式 C.强制用户经常修改用户密码 D.强制用户设置复杂度高的密码 如果这道题目自己拿不准&#xff0c;或者…

【WebAR-图像跟踪】在Unity中基于Imagine WebAR实现AR图像识别

写在前面的话 感慨一下&#xff0c; WebXR的发展是真的快&#xff0c;20年的时候&#xff0c;大多都在用AR.js做WebAR。随着WebXR标准发展&#xff0c;现在诸如Threejs、AFrame、Unity等多个平台都支持里WebXR。 本文将介绍在Unity中使用 Image Tracker实现Web端的AR图像识别功…

HTML5实现好看的圣诞节网站源码

HTML5实现好看的圣诞节网站源码 前言一、设计来源1.1 主界面1.2 圣诞节由来界面1.3 圣诞活动界面1.4 圣诞活动门票界面1.5 团队介绍界面1.6 圣诞照片墙界面1.7 圣诞留言界面1.8 圣诞趣事界面1.9 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好…

go下载依赖提示连接失败

1、现象 Go下载模块提示连接失败 dial tcp 142.251.42.241:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.…

分布式事务入门 一

分布式事务入门 一 您好&#xff0c;我是今夜写代码,今天学习下分布式事务相关理论&#xff0c;以及常见的解决方案&#xff0c;为后续掌握Seata分布式事务框奠定基础。 为什么需要分布式事务? 分布式事务主要由于存储资源的分布性&#xff0c;通常涉及多个数据库。 分布式…

VSCode设置Playwright教程

1.安装扩展 打开VS Code&#xff0c;在扩展—>搜索"Playwright Test for VSCode"&#xff0c;点击安装 按快捷键CommandShiftP&#xff0c;输入install playwright&#xff0c;点击安装Playwright 安装成功会有如下提示 2.调试脚本 打开tests/example.spec.ts文…

在一个C工程文件中,如果一个函数要引用另一个文件中的函数,并不是用extern进行声明,而是在包含的头文件中进行声明

在C语言的某个工程中&#xff0c;一个不是主函数的函数如果引用另一个文件中的某个函数&#xff0c;是不是要先用extern引入&#xff1f;比如下面的代码&#xff1a; void DisplayInit(void) {extern void FramebufferInit(void);FramebufferInit(); }在C语言中&#xff0c;不…

Qt天气预报系统设计_更改窗口名称和图标

Qt 天气预报系统 1、更改窗口名称2、更改窗口图标2.1先把资源加入项目2.2选择资源文件更改窗口图标 1、更改窗口名称 更改MainWindow下的windowTitle即可 2、更改窗口图标 2.1先把资源加入项目 右键项目文件夹&#xff0c;选择 Add New... 选择Qt&#xff0c;接着选择 Qt …

数据仓库工具箱—读书笔记02(Kimball维度建模技术概述04、使用一致性维度集成)

Kimball维度建模技术概述 记录一下读《数据仓库工具箱》时的思考&#xff0c;摘录一些书中关于维度建模比较重要的思想与大家分享&#x1f923;&#x1f923;&#x1f923; 第二章前言部分作者提到&#xff1a;技术的介绍应该通过涵盖各种行业的熟悉的用例展开&#xff08;赞同…

Matlab 和 R 语言的数组索引都是从 1 开始,并且是左闭右闭的

文章目录 一、前言二、主要内容三、小结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 在早期的计算机科学中&#xff0c;数组索引从 1 开始是很常见的。例如&#xff0c;Fortran 和 Pascal 等编程语言也采用了从 1 开始的索引。 这种索引…

Redis - Token JWT 概念解析及双token实现分布式session存储实战

Token 定义&#xff1a;令牌&#xff0c;访问资源接口&#xff08;API&#xff09;时所需要的资源凭证 一、Access Token 定义&#xff1a;访问资源接口&#xff08;API&#xff09;时所需要的资源凭证&#xff0c;存储在客户端 组成 组成部分说明uid用户唯一的身份标识time…

vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)

1.安装video-player npm install video.js videojs-player/vue --save在main.js中配置全局引入 // 导入视频播放组件 import VueVideoPlayer from videojs-player/vue import video.js/dist/video-js.cssconst app createApp(App) // 视频播放组件 app.use(VueVideoPlayer)2…

数据库MySQL(1)

一、数据库简介 数据库是一类软件&#xff0c;其作用就是更加高效的组织数据&#xff0c;我们要讲的数据库软件是MySQL&#xff0c;MySQL所使用的是sql语句&#xff0c;SQL语句就是专门操作数据库的编程语言 数据库组织形式为&#xff1a;数据库→表→行→列 每一个数据库里…

启动动效流程梳理(二)

performSurfacePlacementNoTrace() 这一段从performSurfacePlacement()开始讲起&#xff0c;因为在trace中可以看到在SystemServer中&#xff0c;动效会从performSurfacePlacement这个tag点触发。这里的流程就是在窗口状态改变之后&#xff0c;会触发performSurfacePlacement流…