第一次个人编程作业
第一次个人编程作业
GitHub链接:https://github.com/1534063091/3122004774
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 6 | 8 |
Estimate | 估计这个任务需要多少时间 | 6 | 8 |
Development | 开发 | 127 | 595 |
Analysis | 需求分析 (包括学习新技术) | 10 | 6 |
Design Spec | 生成设计文档 | 6 | 5 |
Design Review | 设计复审 | 6 | 4 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 3 | 3 |
Design | 具体设计 | 12 | 60 |
Coding | 具体编码 | 21 | 480 |
Code Review | 代码复审 | 9 | 7 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 124 | 305 |
Test Repor | 测试报告 | 120 | 300 |
Size Measurement | 计算工作量 | 1 | 2 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 3 | 3 |
合计 | 257 | 908 |
二、计算模块接口的设计与实现过程
1.开发环境
- Windows 11
- jdk-22
2.开发工具
- IntelliJ IDEA 2023.2
- jprofiler14
3.项目依赖
- JUnit框架
4.项目结构
Main
主要功能:主程序入口,处理命令行参数,读取文件,计算相似度,并写入输出文件。
实现过程:
- 检查参数
- 确保命令行参数数量为 3。若不符,输出使用说明并返回。
- 文件路径设置
- 从命令行参数中读取原始文件路径、抄袭文件路径和输出文件路径。
- 验证文件存在性
- 检查原始文件和抄袭文件是否存在。
- 若文件不存在,通过日志记录错误并返回。
- 读取文件内容
- 使用 FileUtils.readFile 方法读取原始文件和抄袭文件的内容。
- 计算相似度
- 使用 StringUtils.calculateSimilarity 方法计算两个文件内容的相似度百分比。
- 写入输出文件
- 相似度结果格式化为保留两位小数的字符串,并使用 FileUtils.writeFile 方法写入指定的输出文件。
- 异常处理
- 捕获 IOException 异常,记录异常信息到日志中。
StringUtils
1.calculateSimilarity(String s1, String s2):
主要功能:计算两个字符串的相似度百分比。
实现过程:
- 输入验证
- 调用 validateInputs(s1, s2) 方法,检查字符串是否为 null。若有 null 值,抛出 IllegalArgumentException 异常。
- 完全匹配检查
- 如果两个字符串相等,返回 100.0(表示完全相同)。
- 计算 Damerau-Levenshtein 距离
- 调用 computeDamerauLevenshteinDistance(s1, s2) 方法计算两个字符串之间的编辑距离。
- 计算相似度百分比
- 使用公式 (1 - (double) distance / maxLength) * 100 计算相似度百分比,其中 maxLength 是两个字符串长度的较大值。
- 特殊情况:如果最大长度为 0,直接返回 100.0。
2.validateInputs(String s1, String s2):
主要功能:验证输入字符串的有效性。
实现过程:
- 检查 s1 和 s2 是否为 null。
- 如果任一字符串为 null,抛出 IllegalArgumentException 异常。
3.computeDamerauLevenshteinDistance(String s1, String s2):
主要功能:计算两个字符串之间的 Damerau-Levenshtein 距离。
实现过程:
- 调整字符串顺序
- 确保 s1 的长度不大于 s2,通过递归调用 computeDamerauLevenshteinDistance 方法交换参数以减少计算复杂度。
- 处理空字符串
- 如果 s1 为空,返回 s2 的长度作为编辑距离(需要的插入操作数)。
- 初始化矩阵
- 创建两个一维数组 prevRow 和 currRow 用于动态规划。
- 调用 initializeDistanceMatrix(prevRow, len1) 方法初始化第一行。
- 动态规划计算
- 遍历 s2 的每个字符,计算将 s1 转化为 s2 的每个前缀所需的编辑距离。
- 处理插入、删除、替换及相邻字符交换操作,更新 currRow 的值。
- 将 prevRow 和 currRow 进行交换,以便于下一个字符的计算。
- 返回结果
- prevRow[len1] 保存了最终的 Damerau-Levenshtein 距离值。
4.initializeDistanceMatrix(int[] row, int length):
主要功能: 初始化距离矩阵的第一行。
实现过程: 为矩阵的第一行赋值,表示从空字符串到任意前缀的编辑距离(即插入操作数)。
FileUtils
1.readFile(String filePath):
主要功能: 读取指定路径的文件内容。
实现过程:
- 创建文件路径对象
- 使用 Paths.get(filePath) 创建 Path 对象表示文件路径。
- 检查文件是否存在
- 使用 Files.exists(path) 检查文件是否存在。如果不存在,则抛出 IOException 异常,带有文件未找到的错误信息。
- 读取文件内容
- 使用 Files.readString(path, StandardCharsets.UTF_8) 方法读取文件内容,并指定使用 UTF-8 编码。
- 返回文件内容
- 返回读取到的文件内容字符串。
2.writeFile(String filePath, String content):
主要功能: 将内容写入指定路径的文件。
实现过程:
- 创建文件路径对象
- 使用 Paths.get(filePath) 创建 Path 对象表示文件路径。
- 检查是否为目录
- 使用 Files.isDirectory(path) 检查指定路径是否是一个目录。如果是目录,则抛出 IOException 异常,带有路径指向目录的错误信息。
- 创建父目录
- 使用 Files.createDirectories(path.getParent()) 创建文件的父目录(如果不存在)。这确保了在写入文件之前,所有必要的目录都已存在。
- 写入文件内容
- 使用 Files.writeString(path, content, StandardCharsets.UTF_8) 方法将内容写入文件,并指定使用 UTF-8 编码。这会覆盖文件中的现有内容。
三、计算模块接口部分的性能改进
1.改进思路
- 优化空间复杂度
- 提前终止
- 改进异常处理
- 算法改进
- 代码可读性和维护
2.性能分析图
四、计算模块部分单元测试展示
1.测试函数
-
testFilesWithSameContent():测试两个文件内容完全相同的情况
-
testFilesWithDifferentContent():测试两个文件内容不同的情况
-
testEmptyFileAndNonEmptyFile():测试一个空文件和一个非空文件的情况
-
testTwoEmptyFiles():测试两个空文件的情况
-
testSingleCharacterFiles():测试两个仅有一个字符的文件
2.测试覆盖率
五、计算模块部分异常处理说明
1.命令行参数数量验证
if (args.length != 3) {
System.out.println("Usage: java -jar main.jar <orig_file> <plagiarized_file> <output_file>");
return;
}
- 处理内容:检查命令行参数的数量是否为 3。
- 异常情况:如果参数数量不正确,打印用法说明并终止程序。
- 异常处理:这种情况属于参数错误,不涉及异常处理,而是直接通过输出消息来指导用户。
2.文件存在性检查
Path origPath = Paths.get(origFilePath);
if (!Files.exists(origPath)) {
logger.severe("Original file does not exist: " + origFilePath);
return;
}
Path plagiarizedPath = Paths.get(plagiarizedFilePath);
if (!Files.exists(plagiarizedPath)) {
logger.severe("Plagiarized file does not exist: " + plagiarizedFilePath);
return;
}
- 处理内容:检查原始文件和抄袭文件是否存在。
- 异常情况:如果文件不存在,使用 logger.severe 记录错误消息,并终止程序。
- 异常处理:这部分不是捕获异常,而是检查文件的存在性并记录相关信息。此处理确保程序在尝试读取文件之前能够正确识别文件的可用性。
3.文件读取和写入操作
String originalText = FileUtils.readFile(origFilePath);
String plagiarizedText = FileUtils.readFile(plagiarizedFilePath);
String result = String.format("%.2f", similarity);
FileUtils.writeFile(outputFilePath, result);
- 处理内容:读取文件内容和将结果写入输出文件。
- 异常情况:如果文件操作(读取或写入)出现问题,会抛出 IOException。
- 异常处理:IOException 被捕获并记录详细的错误信息。
catch (IOException e) {
logger.log(Level.SEVERE, "An error occurred while processing files", e);
}
- 异常类型:IOException
- 处理方式:使用 logger.log 记录异常信息,包括堆栈跟踪,以便调试和问题诊断。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步