• 软工实践作业(五)



    GitHub
    作业链接
    结对博客 031602334


    具体分工

    许郁杨:WordCount代码、文档编写;
    温伊倩:爬虫、附加功能设计和实现、部分文档编写.

    我们首先详细阅读了作业要求,明确了各部分功能、实现方式和细节,以及所需的附加功能。

    确定好需求和设计细节后,我们开始准备实现各自负责的部分,学习和测试需要使用到的技术。接着便是逐步完成各个功能,进行性能分析和单元测试,并编写博客。

    在WordCount部分,我主要是作为“驾驶员”(Driver),而队友主要作为“领航员”(Navigator);
    在爬虫和附加功能部分,我主要是作为“领航员”(Navigator),而队友主要作为“驾驶员”(Driver)。

    这样分工使得两人工作量较为均等,并且各自都能完成较为擅长的部分,保证了最后的质量。


    PSP表格

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 45 57
    · Estimate · 估计这个任务需要多少时间 45 57
    Development 开发 890 1015
    · Analysis · 需求分析 (包括学习新技术) 200 214
    · Design Spec · 生成设计文档 30 32
    · Design Review · 设计复审 20 9
    · Coding Standard · 代码规范(为目前的开发制定合适的规范) 10 5
    · Design · 具体设计 30 34
    · Coding · 具体编码 400 518
    · Code Review · 代码复审 50 41
    · Test · 测试(自我测试,修改代码,提交修改) 150 162
    Reporting 报告 70 47
    · Test Report · 测试报告 20 12
    · Size Measurement · 计算工作量 20 11
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 24
    合计 1005 1119

    代码规范

    代码规范我们用的是实验室的代码规范:阿里巴巴的码出高效,并加上了一些补充


    解题思路与设计说明


    爬虫使用

    • 介绍

    爬虫是用java的一款HTML解析器——Jsoup来实现的。
    首先用Jsoup.connect(String url)抓取url,得到网站的HTML文件文档,根据代码发现各论文页面的地址放置于ptitle类a元素的href属性中,使用Element类得到论文页面的超链接后,再循环得到各论文页面的文档,使用Element类找到papertitle和abstract,将其文本存于字符串中输出到文件中。

    • 流程图

    此处输入图片的描述

    • 主要代码
      //从URL加载Document
                Document doc = Jsoup.connect(URL)
                        // 取消获取相应内容大小限制
                        .maxBodySize(0)
                        //设置超时时间
                        .timeout(600000)
                        .get();
                //ptitle类
                Elements paper = doc.select("[class=ptitle]");
                //ptitle类中带有href属性的a元素
                Elements links = paper.select("a[href]");
                //论文计数
                long count = 0;
                for(Element link : links) {
                    //论文页url
                    String url = link.absUrl("href");
                    Document paperDoc = Jsoup.connect(url)
                            .maxBodySize(0)
                            .timeout(600000)
                            .get();
                    //获取论文标题
                    Elements paperTitle = paperDoc.select("[id=papertitle]");
                    String title = paperTitle.text();
                    //获取论文简介
                    Elements paperAbstract = paperDoc.select("[id=abstract]");
                    String abstracts = paperAbstract.text();
    

    代码组织与内部实现设计(类图)

    此处输入图片的描述


    算法关键


    实现方法

    这次基本要求里相对上次新增加的功能主要有以下几点:

    • 可传入多参数;
    • 可指定输入输出文件;
    • 词频统计权重开关;
    • 可指定词频统计输出个数;
    • 可统计词组词频,并可指定词组长度;
    • 可切换单词词频和词组词频;

    其中实现的关键点在于多参数词组词频统计

    对于多参数,我的实现方式是使用Apache的commons-cli包,并增设JavaBean "WordCounterInfo"

    通过commons-cli中的PosixParser解析原始命令行数据,然后把解析得到的数据存入Bean。

    其中,Bean所包含的参数有:

    private String inputFile = "input.txt";
    private String outputFile = "output.txt";
    private int weightFactor = 1;
    private int phraseLength = -1;
    private int wordFrequencyOutNum = 10;
    

    对于词组词频统计,我的实现方式通过自动机扫描文本,判断得到单词,并记录下该单词的首尾下标,存入队列。然后判断队列长度,每当长度满足要求时,分别取出头尾两个单词的首下标、尾下标,这样就能定位出一整个词组。同时,在自动机判断过程中,每当在单词与单词的间隔中出现不属于分隔符的字母符号时,就清空队列,避免出现不合法的词组。


    流程图

    此处输入图片的描述


    附加题


    设计的创意独到之处

    1.从网站爬取了论文作者、pdf链接的额外信息.[内容txt文件](https://files.cnblogs.com/files/qvq-qvq/result.txt.zip) 2.分析了论文列表中第一作者与第二作者之间的合作关系,并根据关系生成了关系图谱。

    实现思路

    1.实现方法如同爬虫使用。 2.由爬取的论文作者中提取第一作者与第二作者,用Java将之写入到Excel表格中,再使用NodeXL生成关系图表,对生成图进行筛选,可以得到合作数较多的作者。

    实现成果展示

    1.爬取信息图

    此处输入图片的描述

    2.全部作者关系图

    此处输入图片的描述

    筛选掉近发表过一次的作者

    此处输入图片的描述

    细节图(单节点说明此作者的度>=2,但队友都被滤掉了):

    此处输入图片的描述

    身为第一第二作者且和人合作次数最多的作者

    此处输入图片的描述


    关键代码

    这里贴出多参数解析词组词频统计两个关键部分的代码,并做更详尽的分析解释。

    对于多参数解析,我首先添加各项参数及其对应解释,如对于参数i,其意义为"input",后跟数据——输入文件的文件名,具体描述为"input file path."。

    然后创建Posix形式的解析器,并解析命令行。接着逐项处理参数,对于i、o、w三个必有参数,直接取值并存入Bean中;对于其他可选参数,逐项判断。

        /**
         * 解析命令行
         * 
         * @param args 命令行参数
         * @param wordCounterInfo 计数器的Bean
         */
        public static void parseCommadLine(String[] args, WordCounterInfo wordCounterInfo) {
            Options options = new Options();
            options.addOption("i", "input", true, "input file path.");
            options.addOption("o", "output", true, "result file path.");
            options.addOption("w", "weight", true, "set weight factor.");
            options.addOption("m", "length", true, "phrase length.");
            options.addOption("n", "number", true, "word frequency output number.");
            options.addOption("h", "help", false, "print options' information");
    
            CommandLineParser parser = new PosixParser();
            try {
                CommandLine commandLine = parser.parse(options, args);
                if (commandLine.hasOption("h")) {
                    HelpFormatter helpFormatter = new HelpFormatter();
                    helpFormatter.printHelp("Options", options);
                } else {
                    wordCounterInfo.setInputFile(commandLine.getOptionValue("i"));
                    wordCounterInfo.setOutputFile(commandLine.getOptionValue("o"));
                    wordCounterInfo.setWeightFactor(Integer.parseInt(commandLine.getOptionValue("w")));
                    if (commandLine.hasOption("m")) {
                        wordCounterInfo.setPhraseLength(Integer.parseInt(commandLine.getOptionValue("m")));
                    }
                    if (commandLine.hasOption("n")) {
                        wordCounterInfo.setWordFrequencyOutNum(Integer.parseInt(commandLine.getOptionValue("n")));
                    }
                }
            } catch (ParseException e) {
                System.out.println("Arguments format wrong.");
                e.printStackTrace();
            }
        }
    

    对于词组词频统计,基本逻辑与之前处理单词词频时相近。首先读入文本,然后判断是否为Title或Abstract,对这两个部分的文本区分处理。

    通过自动机扫描单词,并记录下单词的首尾下标。每扫描出一个单词,就去构造词组,将新得到的单词压入队列尾部。自动机扫描过程中,如果出现一个合法单词后跟着一个非法单词的情况,就清空队列(此时那个合法单词不能在后续过程中组成合法词组)。如果队列长度满足要求,就记录下词组的首尾下标,并推出头元素。然后根据下标取出词组,存入Map。如果有权重要求,就对Title部分的词组增加权重值。最后对得到的Map排序,就得到了所需的词频排序列表。

        /**
         * 读取并计算Title和Abstract词组词频.
         *
         * @param fileName     文件名
         * @param weightFactor 权重参数
         * @param phraseLength 词组长度
         * @return 各词组词频
         */
        public static HashMap<String, Long> countPhraseFrequency(String fileName, int weightFactor, int phraseLength) {
            InputStreamReader inputStreamReader = null;
            BufferedReader bufferedReader = null;
            String in = "";
            char temp;
            int state = 0;
            int startSubscript = 0;
            int endSubscript = 0;
            HashMap<String, Long> phraseMap = new HashMap<String, Long>(100 * 1024 * 1024);
    
            //读入文件
            try {
                inputStreamReader = new InputStreamReader(new FileInputStream(fileName));
            } catch (FileNotFoundException e) {
                System.out.println("PhraseFrequencyCounter找不到此文件");
                e.printStackTrace();
            }
            if (inputStreamReader != null) {
                bufferedReader = new BufferedReader(inputStreamReader);
            }
            //计算单词词频
            try {
                while ((in = bufferedReader.readLine()) != null) {
                    if (in.contains("Title: ")) {
                        wordsDeque.clear();
                        int length = in.length();
                        state = 0;
                        for (int i = 7; i < length; i++) {
                            temp = in.charAt(i);
                            //大写字母转为小写字母
                            if ((temp >= 65) && (temp <= 90)) {
                                temp += 32;
                            }
                            //自动机状态转移
                            switch (state) {
                                case 0: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        startSubscript = i;
                                        state = 1;
                                    }
                                    break;
                                }
                                case 1: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        state = 2;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 2: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        state = 3;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 3: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        endSubscript = i;
                                        state = 4;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 4: {
                                    if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                        endSubscript = i;
                                    } else {
                                        if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                            StringBuilder phrase = new StringBuilder();
                                            int start = phraseInfo.getStartSubscript();
                                            int end = phraseInfo.getEndSubscript();
                                            char tempc;
                                            for (int j = start; j <= end; j++) {
                                                tempc = in.charAt(j);
                                                if ((tempc >= 65) && (tempc <= 90)) {
                                                    tempc += 32;
                                                }
                                                phrase.append(tempc);
                                            }
                                            if (weightFactor == 1) {
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 10L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 10L);
                                                }
                                            } else {
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 1L);
                                                }
                                            }
                                        }
                                        state = 0;
                                    }
                                    break;
                                }
                            }
                        }
                        if (state == 4) {
                            if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                StringBuilder phrase = new StringBuilder();
                                int start = phraseInfo.getStartSubscript();
                                int end = phraseInfo.getEndSubscript();
                                char tempc;
                                for (int j = start; j <= end; j++) {
                                    tempc = in.charAt(j);
                                    if ((tempc >= 65) && (tempc <= 90)) {
                                        tempc += 32;
                                    }
                                    phrase.append(tempc);
                                }
                                if (weightFactor == 1) {
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 10L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 10L);
                                    }
                                } else {
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 1L);
                                    }
                                }
                            }
                        }
                    } else {
                        if (in.contains("Abstract: ")) {
                            wordsDeque.clear();
                            int length = in.length();
                            state = 0;
                            for (int i = 10; i < length; i++) {
                                temp = in.charAt(i);
                                //大写字母转为小写字母
                                if ((temp >= 65) && (temp <= 90)) {
                                    temp += 32;
                                }
                                //自动机状态转移
                                switch (state) {
                                    case 0: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            startSubscript = i;
                                            state = 1;
                                        }
                                        break;
                                    }
                                    case 1: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            state = 2;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 2: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            state = 3;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 3: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            endSubscript = i;
                                            state = 4;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 4: {
                                        if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                            endSubscript = i;
                                        } else {
                                            if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                                StringBuilder phrase = new StringBuilder();
                                                int start = phraseInfo.getStartSubscript();
                                                int end = phraseInfo.getEndSubscript();
                                                char tempc;
                                                for (int j = start; j <= end; j++) {
                                                    tempc = in.charAt(j);
                                                    if ((tempc >= 65) && (tempc <= 90)) {
                                                        tempc += 32;
                                                    }
                                                    phrase.append(tempc);
                                                }
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 1L);
                                                }
                                            }
                                            state = 0;
                                        }
                                        break;
                                    }
                                }
                            }
                            if (state == 4) {
                                if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                    StringBuilder phrase = new StringBuilder();
                                    int start = phraseInfo.getStartSubscript();
                                    int end = phraseInfo.getEndSubscript();
                                    char tempc;
                                    for (int j = start; j <= end; j++) {
                                        tempc = in.charAt(j);
                                        if ((tempc >= 65) && (tempc <= 90)) {
                                            tempc += 32;
                                        }
                                        phrase.append(tempc);
                                    }
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 1L);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return phraseMap;
        }
    
        /**
         * 构造词组
         * 
         * @param startSubscript 单词首下标
         * @param endSubscript 单词尾下标
         * @param phraseLength 词组长度
         * @return 当前是否构造出合法词组
         */
        private static boolean constructPhrase(int startSubscript, int endSubscript, int phraseLength) {
            WordInfo wordInfo = new WordInfo();
            wordInfo.setStartSubscript(startSubscript);
            wordInfo.setEndSubscript(endSubscript);
    
            wordsDeque.addLast(wordInfo);
            if (wordsDeque.size() == phraseLength) {
                phraseInfo.setStartSubscript(wordsDeque.getFirst().getStartSubscript());
                phraseInfo.setEndSubscript(wordsDeque.getLast().getEndSubscript());
                wordsDeque.removeFirst();
                return true;
            }
            return false;
        }
    

    性能分析

    下面是命令行参数为"-i result.txt -o output.txt -w 1 -n 20"的性能分析情况。

    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述

    下面是命令行参数为"-i result.txt -o output.txt -w 1 -n 20 -m 3"的性能分析情况。

    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述
    此处输入图片的描述

    可以看出消耗最高的为单词和词组的词频统计部分。

        /**
         * 读取并计算Title和Abstract词频.
         *
         * @param fileName     文件名
         * @param weightFactor 权重参数
         * @return 各单词词频
         */
        public static HashMap<String, Long> countWordsFrequency(String fileName, int weightFactor) {
            InputStreamReader inputStreamReader = null;
            BufferedReader bufferedReader = null;
            String in = "";
            char temp;
            int state = 0;
            StringBuilder word = new StringBuilder();
            HashMap<String, Long> wordMap = new HashMap<String, Long>(100 * 1024 * 1024);
    
            //读入文件
            try {
                inputStreamReader = new InputStreamReader(new FileInputStream(fileName));
            } catch (FileNotFoundException e) {
                System.out.println("WordsFrequencyCounter找不到此文件");
                e.printStackTrace();
            }
            if (inputStreamReader != null) {
                bufferedReader = new BufferedReader(inputStreamReader);
            }
            //计算单词词频
            try {
                while ((in = bufferedReader.readLine()) != null) {
                    if (in.contains("Title: ")) {
                        word.setLength(0);
                        int length = in.length();
                        state = 0;
                        for (int i = 7; i < length; i++) {
                            temp = in.charAt(i);
                            //大写字母转为小写字母
                            if ((temp >= 65) && (temp <= 90)) {
                                temp += 32;
                            }
                            //自动机状态转移
                            switch (state) {
                                case 0: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        word.append(temp);
                                        state = 1;
                                    }
                                    break;
                                }
                                case 1: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        word.append(temp);
                                        state = 2;
                                    } else {
                                        word.setLength(0);
                                        state = 0;
                                    }
                                    break;
                                }
                                case 2: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        word.append(temp);
                                        state = 3;
                                    } else {
                                        word.setLength(0);
                                        state = 0;
                                    }
                                    break;
                                }
                                case 3: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        word.append(temp);
                                        state = 4;
                                    } else {
                                        word.setLength(0);
                                        state = 0;
                                    }
                                    break;
                                }
                                case 4: {
                                    if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                        word.append(temp);
                                    } else {
                                        if (weightFactor == 1) {
                                            if (wordMap.containsKey(word.toString())) {
                                                wordMap.put(word.toString(), wordMap.get(word.toString()) + 10L);
                                            } else {
                                                wordMap.put(word.toString(), 10L);
                                            }
                                        } else {
                                            if (wordMap.containsKey(word.toString())) {
                                                wordMap.put(word.toString(), wordMap.get(word.toString()) + 1L);
                                            } else {
                                                wordMap.put(word.toString(), 1L);
                                            }
                                        }
                                        word.setLength(0);
                                        state = 0;
                                    }
                                    break;
                                }
                            }
                        }
                        if (state == 4) {
                            if (weightFactor == 1) {
                                if (wordMap.containsKey(word.toString())) {
                                    wordMap.put(word.toString(), wordMap.get(word.toString()) + 10L);
                                } else {
                                    wordMap.put(word.toString(), 10L);
                                }
                            } else {
                                if (wordMap.containsKey(word.toString())) {
                                    wordMap.put(word.toString(), wordMap.get(word.toString()) + 1L);
                                } else {
                                    wordMap.put(word.toString(), 1L);
                                }
                            }
                        }
                    } else {
                        if (in.contains("Abstract: ")) {
                            word.setLength(0);
                            int length = in.length();
                            state = 0;
                            for (int i = 10; i < length; i++) {
                                temp = in.charAt(i);
                                //大写字母转为小写字母
                                if ((temp >= 65) && (temp <= 90)) {
                                    temp += 32;
                                }
                                //自动机状态转移
                                switch (state) {
                                    case 0: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            word.append(temp);
                                            state = 1;
                                        }
                                        break;
                                    }
                                    case 1: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            word.append(temp);
                                            state = 2;
                                        } else {
                                            word.setLength(0);
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 2: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            word.append(temp);
                                            state = 3;
                                        } else {
                                            word.setLength(0);
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 3: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            word.append(temp);
                                            state = 4;
                                        } else {
                                            word.setLength(0);
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 4: {
                                        if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                            word.append(temp);
                                        } else {
                                            if (wordMap.containsKey(word.toString())) {
                                                wordMap.put(word.toString(), wordMap.get(word.toString()) + 1L);
                                            } else {
                                                wordMap.put(word.toString(), 1L);
                                            }
                                            word.setLength(0);
                                            state = 0;
                                        }
                                        break;
                                    }
                                }
                            }
                            if (state == 4) {
                                if (wordMap.containsKey(word.toString())) {
                                    wordMap.put(word.toString(), wordMap.get(word.toString()) + 1L);
                                } else {
                                    wordMap.put(word.toString(), 1L);
                                }
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return wordMap;
        }
        
        /**
         * 读取并计算Title和Abstract词组词频.
         *
         * @param fileName     文件名
         * @param weightFactor 权重参数
         * @param phraseLength 词组长度
         * @return 各词组词频
         */
        public static HashMap<String, Long> countPhraseFrequency(String fileName, int weightFactor, int phraseLength) {
            InputStreamReader inputStreamReader = null;
            BufferedReader bufferedReader = null;
            String in = "";
            char temp;
            int state = 0;
            int startSubscript = 0;
            int endSubscript = 0;
            HashMap<String, Long> phraseMap = new HashMap<String, Long>(100 * 1024 * 1024);
    
            //读入文件
            try {
                inputStreamReader = new InputStreamReader(new FileInputStream(fileName));
            } catch (FileNotFoundException e) {
                System.out.println("PhraseFrequencyCounter找不到此文件");
                e.printStackTrace();
            }
            if (inputStreamReader != null) {
                bufferedReader = new BufferedReader(inputStreamReader);
            }
            //计算单词词频
            try {
                while ((in = bufferedReader.readLine()) != null) {
                    if (in.contains("Title: ")) {
                        wordsDeque.clear();
                        int length = in.length();
                        state = 0;
                        for (int i = 7; i < length; i++) {
                            temp = in.charAt(i);
                            //大写字母转为小写字母
                            if ((temp >= 65) && (temp <= 90)) {
                                temp += 32;
                            }
                            //自动机状态转移
                            switch (state) {
                                case 0: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        startSubscript = i;
                                        state = 1;
                                    }
                                    break;
                                }
                                case 1: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        state = 2;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 2: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        state = 3;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 3: {
                                    if ((temp >= 97) && (temp <= 122)) {
                                        endSubscript = i;
                                        state = 4;
                                    } else {
                                        wordsDeque.clear();
                                        state = 0;
                                    }
                                    break;
                                }
                                case 4: {
                                    if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                        endSubscript = i;
                                    } else {
                                        if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                            StringBuilder phrase = new StringBuilder();
                                            int start = phraseInfo.getStartSubscript();
                                            int end = phraseInfo.getEndSubscript();
                                            char tempc;
                                            for (int j = start; j <= end; j++) {
                                                tempc = in.charAt(j);
                                                if ((tempc >= 65) && (tempc <= 90)) {
                                                    tempc += 32;
                                                }
                                                phrase.append(tempc);
                                            }
                                            if (weightFactor == 1) {
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 10L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 10L);
                                                }
                                            } else {
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 1L);
                                                }
                                            }
                                        }
                                        state = 0;
                                    }
                                    break;
                                }
                            }
                        }
                        if (state == 4) {
                            if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                StringBuilder phrase = new StringBuilder();
                                int start = phraseInfo.getStartSubscript();
                                int end = phraseInfo.getEndSubscript();
                                char tempc;
                                for (int j = start; j <= end; j++) {
                                    tempc = in.charAt(j);
                                    if ((tempc >= 65) && (tempc <= 90)) {
                                        tempc += 32;
                                    }
                                    phrase.append(tempc);
                                }
                                if (weightFactor == 1) {
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 10L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 10L);
                                    }
                                } else {
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 1L);
                                    }
                                }
                            }
                        }
                    } else {
                        if (in.contains("Abstract: ")) {
                            wordsDeque.clear();
                            int length = in.length();
                            state = 0;
                            for (int i = 10; i < length; i++) {
                                temp = in.charAt(i);
                                //大写字母转为小写字母
                                if ((temp >= 65) && (temp <= 90)) {
                                    temp += 32;
                                }
                                //自动机状态转移
                                switch (state) {
                                    case 0: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            startSubscript = i;
                                            state = 1;
                                        }
                                        break;
                                    }
                                    case 1: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            state = 2;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 2: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            state = 3;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 3: {
                                        if ((temp >= 97) && (temp <= 122)) {
                                            endSubscript = i;
                                            state = 4;
                                        } else {
                                            wordsDeque.clear();
                                            state = 0;
                                        }
                                        break;
                                    }
                                    case 4: {
                                        if (((temp >= 97) && (temp <= 122)) || ((temp >= '0') && (temp <= '9'))) {
                                            endSubscript = i;
                                        } else {
                                            if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                                StringBuilder phrase = new StringBuilder();
                                                int start = phraseInfo.getStartSubscript();
                                                int end = phraseInfo.getEndSubscript();
                                                char tempc;
                                                for (int j = start; j <= end; j++) {
                                                    tempc = in.charAt(j);
                                                    if ((tempc >= 65) && (tempc <= 90)) {
                                                        tempc += 32;
                                                    }
                                                    phrase.append(tempc);
                                                }
                                                if (phraseMap.containsKey(phrase.toString())) {
                                                    phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                                } else {
                                                    phraseMap.put(phrase.toString(), 1L);
                                                }
                                            }
                                            state = 0;
                                        }
                                        break;
                                    }
                                }
                            }
                            if (state == 4) {
                                if (constructPhrase(startSubscript, endSubscript, phraseLength)) {
                                    StringBuilder phrase = new StringBuilder();
                                    int start = phraseInfo.getStartSubscript();
                                    int end = phraseInfo.getEndSubscript();
                                    char tempc;
                                    for (int j = start; j <= end; j++) {
                                        tempc = in.charAt(j);
                                        if ((tempc >= 65) && (tempc <= 90)) {
                                            tempc += 32;
                                        }
                                        phrase.append(tempc);
                                    }
                                    if (phraseMap.containsKey(phrase.toString())) {
                                        phraseMap.put(phrase.toString(), phraseMap.get(phrase.toString()) + 1L);
                                    } else {
                                        phraseMap.put(phrase.toString(), 1L);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return phraseMap;
        }
    

    对于词组词频统计部分,我们一开始的想法是通过双重循环,对每个单词都去判断是否能以其为首组成词组。维护一个词组的字符串,当符合长度时便存入Map。这种做法虽然简单,但加上中间操作后的时间消耗比较大。所以经过试验,对其进行了改进,改为记录首尾下标,这样减少时间消耗,也能保存下单词间的分隔符。


    单元测试

    单元测试框架用的是JUnit4。
    我总共设计了十二个单元测试,其中Main一个,三个字词计数部分各三个,单词词频计数部分一个,词组词频计数部分一个。

    单元测试 测试项 被测试代码
    CharCounterTest 分别测试普通字符、无标题无摘要和空格 CharCounter.java
    WordCounterTest 分别测试普通单词、无标题无摘要和大小写单词 WordCounter.java
    LineCounterTest 分别测试普通行、无标题无摘要和混合行 LineCounter.java
    WordFrequencyCounterTest 测试混合单词 WordFrequencyCounter.java
    PhraseFrequencyCounterTest 测试混合词组 PhraseFrequencyCounter.java
    MainTest 测试空白文件 Main.java

    此处输入图片的描述

    部分测试代码

    import com.eventide.wordCount.dataprocess.service.WordCounter;
    import org.junit.Test;
    
    import static org.junit.Assert.assertEquals;
    
    public class WordCounterTest {
        @Test
        //测试正常单词
        public void wordCounterTest1() {
            long wordNum = WordCounter.countWord("normalWordTest.txt");
            assertEquals(3, wordNum);
        }
    
        @Test
        //测试无标题无摘要单词
        public void wordCounterTest2() {
            long wordNum = WordCounter.countWord("noTitleAbstractWordTest.txt");
            assertEquals(0, wordNum);
        }
    
        @Test
        //测试单词大小写
        public void wordCounterTest3() {
            long wordNum = WordCounter.countWord("upLowWordTest.txt");
            assertEquals(4, wordNum);
        }
    }
    
    

    GitHub签入记录

    此处输入图片的描述
    此处输入图片的描述


    遇到的困难

    对于词组词频统计部分,我们总共尝试了三种方法。

    一种是上文提到的,通过双重循环,对每个单词都去判断是否能以其为首组成词组。维护一个词组的字符串,当符合长度时便存入Map。这种做法虽然简单,但加上中间操作后的时间消耗比较大。

    第二种是维护一个队列,每当有合法单词出现时就压入队列中。如果队列长度符合要求,就取出队列中保存的单词,拼接成词组,压入Map。这种方法虽然简单快捷,但在看到群里说到,不同分隔符算不同词组时就凉了。。如果要记入分隔符就需要将分隔符一起存下来。虽然可以将合法单词和其后的分隔符一起保存,但这种方法在实现上存在一些困难,在拼接成词组时还要对单词进行二次处理,因此我们觉得并不是合适的处理方式。

    经过试验,我们使用了记录首尾下标的方式。创建一个JavaBean存下每个合法单词的首尾下标,通过下标组成队列,进而拼装出词组。这样做效率不错,处理过程也不复杂,因此我们认为是较为合适的方法。

    我们还遇到了一个重大困难,就是国庆假期前两个人接连感冒发烧(都怪优秀的舍友),一直到现在也还没完全好。。我想解决方法,应该只有穿越时空解决掉舍友了吧。(肥宅怎么可能去锻炼身体.jpg


    感想

    我的优秀的队友,擅长Python,爬虫部分高效完成,并且高效改写为Java,然后还高效地搞定了附加题。学习能力强,带病工作效率还恐怖如斯,让生病了就不想干活的我惭愧不已_ (:△」∠) _。

    需要改进的地方就是有时候比较粗心,比如文件丢了之类的。。虽然有时候是软件的问题吧。

    不过这次真是疲倦作业呀。。。


    参考链接

    Java 容器源码分析之 Deque 与 ArrayDeque
    commons-cli使用介绍

  • 相关阅读:
    021.10 IO流 打印流
    1、Node.js 我的开始+安装
    021.9 IO流 流总结
    021.8 properties(开发使用频率高)
    021.7 装饰设计模式
    021.6 IO流 练习
    021.5 IO流——字符流
    scrapy基础知识之 CrawlSpiders爬取lagou招聘保存在mysql(分布式):
    scrapy基础知识之 关于爬虫部分一些建议:
    scrapy基础知识之 处理Redis里的数据:
  • 原文地址:https://www.cnblogs.com/S031602240/p/9780977.html
Copyright © 2020-2023  润新知