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

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

一、需求描述

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

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

常量(文件名称 常量名称 常量类型 常量说明)

变量(文件名称 变量 变量类型 功能说明)

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

二、实现步骤

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

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

  3. 解析每个文件:

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

  5. 导出为 Excel 表格

三、实现代码

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

python 复制代码
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(): 清理和验证标识符

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

python 复制代码
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)
相关推荐
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
Miketutu3 小时前
Spring MVC消息转换器
java·spring
乔冠宇3 小时前
Java手写简单Merkle树
java·区块链·merkle树
小王子10243 小时前
设计模式Python版 组合模式
python·设计模式·组合模式
LUCIAZZZ4 小时前
简单的SQL语句的快速复习
java·数据库·sql
komo莫莫da4 小时前
寒假刷题Day19
java·开发语言
Mason Lin5 小时前
2025年1月22日(网络编程 udp)
网络·python·udp
清弦墨客5 小时前
【蓝桥杯】43697.机器人塔
python·蓝桥杯·程序算法
S-X-S5 小时前
算法总结-数组/字符串
java·数据结构·算法
linwq85 小时前
设计模式学习(二)
java·学习·设计模式