执行效果
python3 find_large_functions.py
./stage_test.cpp: StageTest::process has 106 lines.
./stage_test.cpp: StageTest::do_test_ has 172 lines.
实现方法
在检测到函数开始的“可能”标志后(比如返回类型和作用域运算符::),开始累积接下来的行直到遇到函数体开始的左大括号{为止,从而捕获完整的函数声明。
关键点:
- 使用re.MULTILINE | re.DOTALL标志允许正则表达式跨越多行并正确匹配换行符。
- func_pattern试图匹配整个函数声明,包括可能分布在多行的参数列表,直到声明结束处的左大括号{。
- 在找到一个匹配后,代码将继续搜索匹配的右大括号},以确定函数体的实际长度,并计算行数。
import os
import redef find_large_functions_in_file(file_path, min_lines=100):with open(file_path, 'r', encoding='utf-8') as file:lines = file.read()# 模式匹配类成员函数(可能跨越多行)func_pattern = re.compile(r'\w[\w\s:]*\w+::\w+\s*\([^)]*\)\s*[{]',re.MULTILINE | re.DOTALL)functions = []for match in func_pattern.finditer(lines):func_sig = match.group()start_index = match.start()# 计算左右大括号以平衡,找出函数体的结束brace_count = 1i = match.end()while i < len(lines) and brace_count != 0:if lines[i] == '{':brace_count += 1elif lines[i] == '}':brace_count -= 1i += 1func_body = lines[start_index:i]line_count = func_body.count('\n')if line_count >= min_lines:# 提取简化的函数名func_name_match = re.search(r'(\w+)::(\w+)\s*\(', func_sig)if func_name_match:class_name = func_name_match.group(1)func_name = func_name_match.group(2)display_name = f"{class_name}::{func_name}"else:display_name = "Unknown Function"functions.append((display_name, line_count))return functionsdef find_large_functions(directory, line_threshold=100):for root, dirs, files in os.walk(directory):for file_name in files:if file_name.endswith('.cpp'):file_path = os.path.join(root, file_name)large_functions = find_large_functions_in_file(file_path, line_threshold)for func_name, line_count in large_functions:print(f"{file_path}: {func_name} has {line_count} lines.")if __name__ == "__main__":find_large_functions('.', 50)
改进实现
- 读取整个文件内容:考虑到需要使用正则表达式跨多行匹配函数声明,我们维持了读取整个文件内容的方式,但这在处理巨大的源文件时可能不够高效。
- 正则表达式改进:更好地匹配可能跨越多行的函数参数列表。
- 异常处理: 当文件非常大或者编码问题导致无法打开时,应该添加适当的异常处理来增强代码的健壮性,这在示例中未展示。
- 性能注意事项:对于特别大的项目或文件,完整地读取每个文件进内存可能导致性能瓶颈。对于超大文件,可能需要采用更高效的逐行分析策略,但这可能会复杂化正则表达式匹配跨越多行的情况。
import os
import redef find_large_functions_in_file(file_path, min_lines=100):# 编译正则表达式以匹配C++类成员函数声明(考虑跨多行)func_pattern = re.compile(r'\w[\w\s:]*\w+::\w+\s*\((?:[^)]|\n)*\)\s*{',re.MULTILINE | re.DOTALL)functions = [] # 存储符合条件的函数信息with open(file_path, 'r', encoding='utf-8') as file:content = file.read()for match in func_pattern.finditer(content):start_index = match.end() # 函数体开始位置# 确定函数体结束的位置brace_level = 1 # 已匹配一个左花括号for i in range(start_index, len(content)):if content[i] == '{':brace_level += 1elif content[i] == '}':brace_level -= 1if brace_level == 0: # 所有括号均已匹配end_index = i + 1breakelse:# 如果找不到匹配的右括号,则跳过此函数continue# 计算函数体行数line_count = content[start_index:end_index].count('\n')if line_count >= min_lines:# 提取和格式化函数名func_name_match = re.search(r'(\w+)::(\w+)\s*\(', match.group(0))class_name, func_name = func_name_match.groups() if func_name_match else ("Unknown", "Unknown")display_name = f"{class_name}::{func_name}"functions.append((display_name, line_count))return functionsdef find_large_functions(directory, line_threshold=100):for root, _, files in os.walk(directory):for file_name in files:if file_name.endswith('.cpp'):file_path = os.path.join(root, file_name)large_functions = find_large_functions_in_file(file_path, line_threshold)for func_name, line_count in large_functions:print(f"{file_path}: {func_name} has {line_count} lines.")if __name__ == "__main__":find_large_functions('.', 100)