随笔 - 16,  文章 - 0,  评论 - 0,  阅读 - 361
< 2025年1月 >
29 30 31 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 1
2 3 4 5 6 7 8
这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-12
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13220
这个作业的目标 设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。

1.项目地址:

https://github.com/Nethtra/3122004789

2.PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 60
· Estimate · 估计这个任务需要多少时间 60 60
Development 开发 900 1040
· Analysis · 需求分析 (包括学习新技术) 240 240
· Design Spec · 生成设计文档 60 60
· Design Review · 设计复审 30 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 60 60
· Coding · 具体编码 300 360
· Code Review · 代码复审 60 60
· Test · 测试(自我测试,修改代码,提交修改) 120 200
Reporting 报告 120 120
· Test Repor · 测试报告 30 30
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 60
· 合计 1080 1220

3.接口的设计与实现过程

代码设计模块分为四个类:

Check: 负责文章特征的提取与处理。实现提取文章中相邻的汉字双字词,计算出这些词的哈希值,并存储到一个排序集合中。
Compare: 提供比较两个文章特征集合的功能,计算两个文章共有的双字词数量。
Parse: 处理用户输入,并进行查重分析,调用Check和Compare类来进行文件处理和查重率的计算。
WrongMessage: 用于处理文件操作过程中可能发生的错误,主要是文件读取或写入异常。
关键函数:
Check的构造函数:负责文件的逐行读取和特征提取。
Compare.compare():使用双指针法高效地比较两个文章的特征集合。
Parse.main():处理输入参数,调用Check和Compare,最终输出查重率。

4. 性能分析


性能优化可以集中在以下几个方面:
文件处理效率:通过使用BufferedReader进行逐行读取,减少一次性读取大文件的内存消耗。
特征提取:通过TreeSet存储特征,自动排序避免了手动排序的时间消耗。
特征比较:使用双指针法高效地比较两个排序后的特征集合,时间复杂度为O(n)。

5.单元测试

testAllArticle():
测试整个目录下的所有文件,与原文档进行查重比较,输出查重率。该测试遍历了文件目录,模拟了实际使用中的批量处理情况。
测试数据思路:选择多个文本文件,将一个作为源文件(orig.txt),其余作为对比文件。模拟实际查重的场景,测试整个流程的正确性和稳定性。
testWrongArgs():
测试输入的文件路径不正确时的错误处理。
测试数据思路:故意输入错误的文件路径,验证程序在找不到文件时是否能够正确处理异常并输出错误信息。
testShortArgs():
测试输入参数数量不足时的处理。
测试数据思路:输入仅包含一个参数,检查程序是否能正确识别并提示参数不足。

package test;
/**
 * @version 1.0
 */

import java.io.File;
import java.util.Arrays;
import java.util.Stack;

import com.Parse;
import org.junit.Test;

public class test {
    /**
     * 测试某个目录下所有文件查重率
     */
    @Test
    public void testAllArticle() {
        String basePath = "src\\testfiles\\";
        String resPath = "src\\res.txt";
        String origin = "orig.txt";

        long startTime = System.currentTimeMillis();

        Stack<String> testFileName = getTestFileName(basePath);

        //测试每一个子文件查重率
        while (!testFileName.empty()) {
            String fileName = testFileName.pop();

            if (fileName.equals(origin)) {
                continue;
            }
            String[] args = {
                    basePath + origin,
                    basePath + fileName,
                    resPath
            };
            System.out.println("参数:"+ Arrays.toString(args));
            Parse.main(args);
            System.out.println();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("消耗时间:" + new Double(endTime - startTime) / 1000 + "s");
    }
    /**
     * 测试命令行输入参数错误处理
     */
    @Test
    public void testWrongArgs(){
        String[] args={
                "wrong_origin_path",
                "wrong_target_path",
                "wrong_res_path",
        };
        System.out.println("参数:"+ Arrays.toString(args));
        Parse.main(args);
    }
    /**
     * 测试命令行输入参数错误处理
     */
    @Test
    public void testShortArgs(){
        String[] args={
                "wrong_args_length",
        };
        System.out.println("参数:"+ Arrays.toString(args));
        Parse.main(args);
    }

    /**
     * 仅当使用JProfiler时定义
     * 防止JVM过早退出,导致无法查看信息
     */
//    @Test
//    public void zwaitForJProfiler() {
//        try {
//            Thread.sleep(100 * 1000);
//        }catch (InterruptedException e){
//
//        }
//    }
    /**
     * 获取目录下的所有文件名
     * @param basePath 目录路径
     * @return 目录下的所有文件名
     */
    public static Stack<String> getTestFileName(String basePath){
        Stack<String> res=new Stack<String>();
        File dir = new File(basePath);
        if(dir.isDirectory()) {
            File[] files = dir.listFiles();
            for (File file : files) {
                if(file.isDirectory()){
                }else{
                    res.push(file.getName());
                }
            }
        }
        return res;
    }

}

6.异常处理

  1. ZeroFeature 异常
    设计目标:当无法从文件中提取任何双字词特征时触发该异常。可能的情况包括文件为空,文件只包含标点符号,或文件中的字符不足以组成双字词。
    异常处理逻辑:捕获ZeroFeature异常后,输出文件路径和相关错误信息。
    对应的错误场景:empty.txt为空文件,程序无法提取特征,会抛出ZeroFeature异常,并打印相应的错误提示。

  2. IOException(文件读取/写入异常)
    设计目标:当文件无法打开(路径错误、权限不足等)或写入失败时,捕获IOException异常,避免程序崩溃,并提示用户错误的文件路径。
    异常处理逻辑:在捕获IOException时,调用WrongMessage.openFileError()或WrongMessage.writeFileError(),打印详细的错误信息。
    对应的错误场景:输入了不存在的文件路径,程序无法找到指定文件,会抛出IOException,并打印出错误路径。

  3. 命令行参数不足异常(输入参数错误处理)
    设计目标:当用户未按程序要求提供足够的命令行参数时,程序应当提示参数不足。
    异常处理逻辑:在Parse.main()中,如果args.length不足3,则直接返回并提示输入参数不正确。
    对应的错误场景:输入的参数不足,程序会输出提示信息,提醒用户正确输入参数。

posted on   uabek  阅读(30)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示