解析 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)
相关推荐
tealcwu22 分钟前
【游戏设计原理】32 - 消费者剩余
java·开发语言·游戏
小马爱打代码31 分钟前
Spring Boot项目开发常见问题及解决方案(下)
java·spring boot·后端
潜意识起点33 分钟前
计算机专业文献检索期末论文
java·开发语言
m0_7482359536 分钟前
Python毕业设计选题:基于Hadoop 的国产电影数据分析与可视化_django+spider
hadoop·python·课程设计
岁月如歌,青春不败44 分钟前
R语言森林生态系统结构、功能与稳定性分析与可视化
开发语言·人工智能·python·深度学习·r语言
赵谨言1 小时前
基于 Python 考研历年国家分数线大数据分析设计与实现
经验分享·python·毕业设计
hvinsion1 小时前
Python PDF批量加密工具
android·python·pdf
工业互联网专业1 小时前
Python大数据可视化:基于Python对B站热门视频的数据分析与研究_flask+hive+spider
hive·python·数据分析·flask·毕业设计·源码·spider
Q_19284999061 小时前
基于Spring Boot的工商局商家管理系统
java·spring boot·后端
m0_748232641 小时前
[MySQL报错]关于发生net start mysql 服务无法启动,服务没有报告任何错误的五种解决方案。
java