词频统计

   
这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/computer-science-class3-2018/homework/11879
这个作业有什么要求 https://edu.cnblogs.com/campus/zswxy/computer-science-class3-2018/homework/11879
学号 20188476
  • WordCount

    • Git项目地址

    • PSP表格

    • 代码规范链接

    • 解题思路描述

    • 设计与实现过程

    • 部分异常处理说明

    • 心路历程与收获

Git项目地址

https://gitee.com/BX881206/project-java/tree/master

PSP表格

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

代码规范链接

https://gitee.com/BX881206/project-java/blob/master/20188476/codestyle.md

解题思路描述

  • 读取字符是一个一个读取输入的文件流,对其进行判断是否符合要求之后再进行计数(不符合要求的计数是无效的,必须对其进行判断)
  • 读取单词,可以使用正则表达式来匹配单词,并过滤掉无效字符,以单词-频率作为键值对保存在TreeMap中
  • 行数统计直接按行读取后使用trim()去掉空白字符后,再进行判断计数就可以了

设计与实现过程

  • 类和函数关系图

主要流程图

部分异常处理说明

1.通过CMD运行提示“编码GBK的不可映射字符”

  • 解决办法:查找了很多资料,收获了好几种方法,感觉最好用的是直接对.Java文件改编码方式

    2.使用命令行编译时,由于一开始的项目在SRC目录下有包,.java文件在包里,所以javac 命令要进入到.java文件目录也就是最里层才可以,生成的.class文件和.java文件在同一目录。而使用java命令时,则要进到SRC目录下,包名作为前缀名来使用,并且名称不带后缀,格式如:java 包名.java文件名 才可以运行,然后我就直接重新新建了一个文件,运行成功了.
  • 关键代码

1.统计文件的字符数(对应输出第一行)

  • 思路:用了BufferedReader去读取文本,用while循环去判断读入字符是否合法,最后return读取的字符数(具体可见注释部分)
  • 独到之处:使用BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符,数组和行的高效读取,而且可以指定缓冲区的大小
 public static int charNum(String filename) throws IOException  {
		  //统计文件的字符数
			int num = 0;		
			BufferedReader br = new BufferedReader(new FileReader(filename));
			int value = -1;// EOF标记文件末尾
			while ((value = br.read()) != -1) {
				//判断是否读到了文件末尾
				if (value > 0 && value < 128 && value != 13) {
					//不统计回车符,ASCII正常十进制范围0~127
					num ++;
				}			
			}
			br.close();		
			return num;
		}

2.统计文件的单词总数(对应输出第二行),单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。

  • 思路:用了BufferedReader去读取文本,用正则表达式去判断匹配是否是有效单词,匹配成功则计数(具体可见注释部分)
  • 独到之处:用正则表达式,Pattern类,Matcher类方法去匹配是否是有效单词
public static int wordNum(String filename) throws IOException  {
			//统计单词总数
			int num = 0;			
			BufferedReader br = new BufferedReader(new FileReader(filename));
			String separator = "[^A-Za-z0-9]";//分隔符
			String regex = "^[A-Z a-z]{4,}[0-9]*$"; //正则判断每个数组中是否存在有效单词
			Pattern p = Pattern.compile(regex);//使用Pattern.compile方法编译一个正则表达式,创建一个匹配模式
			Matcher m = null;
			String line = null;
			String[] array = null;
			while ((line  = br.readLine()) != null) {
	            line = line.replaceAll("[(\\u4e00-\\u9fa5)]", "");// 用空格替换汉字
	            line = line.replaceAll(separator, " "); // 用空格替换分隔符
	            array = line.split("\\s+"); // 按空格分割 →字符串数组                  
	            for (int i = 0;i<array.length;i++) { 
	            	m = p.matcher(array[i]);//进行匹配,看是否是有效单词
	            	if (m.matches()) {//匹配成功
	            		num++;
	            	}
	            }
			}						
			br.close();//关闭缓冲区		
			return num;//返回单词数
		}	

3.统计文件的有效行数(对应输出第三行):任何包含非空白字符的行,都需要统计

  • 思路:行数统计直接按行读取后使用trim()函数去掉空白字符后,再对行数进行判断
  • 独到之处:使用了trim()函数去掉了字符串首尾的空格
public static int lineNum(String filename) throws IOException  {
	 		//统计行数
			int num = 0;			
			BufferedReader br = new BufferedReader(new FileReader(filename));
			String line = null;
			while ((line  = br.readLine()) != null) {
				if (line.trim().length() != 0) {//使用trim()函数去掉字符串首尾的空格
					num ++;					
				}
			}
			br.close();		
			return num;
		}	

4.统计文件中各单词的出现次数(对应输出接下来10行),最终只输出频率最高的10个

  • 思路:正则表达式判断匹配单词,匹配成功则计数(具体可见注释部分)
  • 独到之处:单词-次数作为键值对保存在TreeMap中,运用toLowerCase()方法将字符串数组中的元素转换成小写
public static TreeMap<String, Integer> wordMap(String filename) throws IOException {
	        TreeMap<String, Integer> tm = new TreeMap<String, Integer>();				
			BufferedReader br = new BufferedReader(new FileReader(filename));
			String separator = "[^A-Za-z0-9]";//分隔符
			String regex = "^[A-Za-z]{4,}[0-9]*$"; // 正则判断每个数组中是否存在有效单词
			Pattern p = Pattern.compile(regex);//使用Pattern.compile方法编译一个正则表达式,创建一个匹配模式
			String str = null;
			Matcher m = null;
			String line = null;
			String[] array = null;
			while ((line  = br.readLine()) != null) {
	            line = line.replaceAll("[(\\u4e00-\\u9fa5)]", "");// 过滤汉字
	            line = line.replaceAll(separator, " "); // 用空格替换分隔符
	            array = line.split("\\s+"); // 按空格分割                     
	            for (int i = 0;i<array.length;i++) { 
	            	m = p.matcher(array[i]);//
	            	if (m.matches()) {//如果正确匹配
	            		str = array[i].toLowerCase();//运用toLowerCase()方法将字符串数组中的元素转换成小写                
	                    if (!tm.containsKey(str)) {//用containsKey()方法看对应单词是否大小写重复
	                        tm.put(str, 1);//输出对应单词小写
	                    } else {
	                        int count = tm.get(str) + 1;//单词没有重复则单词出现次数+1
	                        tm.put(str, count);//输出对应单词小写和单词出现的次数
	                    }
	            	}
	            }
			}						
			br.close();		
			return tm;
		}

5.对单词进行排序并输出

  • 思路:以单词-频率作为键值对保存在TreeMap中,TreeMap会根据键的字典序自动升序排列,按照频率优先的顺序输出频率最高的10个单词(具体可见注释部分)
  • 独到之处:以单词-频率作为键值对保存在TreeMap中,TreeMap会根据键的字典序自动升序排列
public static void writeMostWord(String infilename,String outfilename) throws IOException {//按照格式输出结果
		String outpath = new File(outfilename).getAbsolutePath();
        FileWriter fw = new FileWriter(outpath, true);
        TreeMap<String, Integer> tm = wordMap(infilename);
		if(tm != null && tm.size()>=1)
		{		  
			List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(tm.entrySet());
			// 通过比较器来实现排序
			Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
				public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
					//treemap默认按照键的字典序升序排列的,所以list也是排过序的,在值相同的情况下不用再给键升序排列		
					// 按照值降序排序 
					return o2.getValue().compareTo(o1.getValue());
				}
			});   
			int i = 1;
			String key = null;
			Integer value = null;
			for (Map.Entry<String, Integer> mapping : list) {
				key = mapping.getKey();
				value = mapping.getValue();
				System.out.print("<" + key + ">: " + value + '\n');
				fw.write("<" + key + ">: " + value + '\n');
				//只输出前10个
				if (i == 10) {
					break;
				}
				i++;
			}
		}
		fw.close();
	}

6.测试程序

  • input文件
  • output文件
  • 运行结果

心路历程与收获

第一眼看到这个作业就蒙了,因为作业描述实在太多了,本能排斥这次作业,就一直拖着,看着离ddl越来越近,在接近只有八天的时候,我坐不住了,就开始着手完成这次作业,查了大量资料,虽然这次作业真的很困难,但感觉自己真的学到了很多,复习了Java语言,学到了很多新的方法,正则表达式的作用太大了,pattern类和matcher类方法很有妙用,经过这次作业,也让我感觉到自身实力的不足,还得多加努力才行.

posted @ 2021-04-02 18:16  福纨  阅读(101)  评论(0)    收藏  举报