一
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/zswxy/computer-science-class4-2018 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/computer-science-class4-2018/homework/11880 |
这个作业的目标 | 写一个能统计出文件中出现最多次数的词 |
其他参考文献 | 构建之法,CSDN,java开发实战经典 |
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 25 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 1100 | 1200 |
Development | 开发 | 300 | 400 |
• Analysis | • 需求分析 (包括学习新技术) | 200 | 350 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 30 | 30 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 30 | 40 |
• Coding | • 具体编码 | 180 | 250 |
• Code Review | • 代码复审 | 60 | 80 |
• Test | • 测试(自我测试,修改代码,提交修改) | 100 | 100 |
Reporting | 报告 | 100 | 120 |
• Test Repor | • 测试报告 | 10 | 10 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1090 | 1460 |
三、gitee地址
https://gitee.com/the-first-semester-of-2021/project-java#project-java
四、需求
实现一个命令行程序,不妨称之为WordCount。
1、实现基本需求
假设有一个软件每隔一小段时间会记录一次用户的搜索记录,记录为英文。
输入文件和输出文件以命令行参数传入。例如我们在命令行窗口(cmd)中输入:
//C语言类
WordCount.exe input.txt output.txt
//Java语言
java WordCount input.txt output.txt
则会统计input.txt中的以下几个指标
统计文件的字符数(对应输出第一行):
只需要统计Ascii码,汉字不需考虑
空格,水平制表符,换行符,均算字符
统计文件的单词总数(对应输出第二行),单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。
英文字母: A-Z,a-z
字母数字符号:A-Z, a-z,0-9
分割符:空格,非字母数字符号
例:file123是一个单词, 123file不是一个单词。file,File和FILE是同一个单词
统计文件的有效行数(对应输出第三行):任何包含非空白字符的行,都需要统计。
统计文件中各单词的出现次数(对应输出接下来10行),最终只输出频率最高的10个。
频率相同的单词,优先输出字典序靠前的单词。
例如,windows95,windows98和windows2000同时出现时,则先输出windows2000
输出的单词统一为小写格式
然后将统计结果输出到output.txt,输出的格式如下;其中word1和word2 对应具体的单词,number为统计出的个数;换行使用' ',编码统一使用UTF-8。
characters: number
words: number
lines: number
word1: number
word2: number
...
2、接口封装
如果现在我们要把这个功能放到不同的环境中去(例如,命令行,Windows图形界面程序,网页程序,手机App),就会碰到困难:代码散落在各个函数中,很难剥离出来作为一个独立的模块运行以满足不同的需求。
这些代码的种类不同,混杂在一起对于后期的维护扩展很不友好,所以它们的组织结构就需要精心的整理和优化。
我们希望把基本功能里的:
统计字符数
统计单词数
统计最多的10个单词及其词频
这三个功能独立出来,成为一个独立的模块(class library, DLL, 或其它),这样的话,命令行和GUI的程序都能使用同一份代码。为了方便起见,我们称之为计算核心"Core模块",这个模块至少可以在几个地方使用:
命令行测试程序使用
在单元测试框架下使用
与数据可视化部分结合使用
把计算核心在单元测试框架中做过完备的测试后,我们就可以在算法层级保证了这个模块的正确性。
但我们知道软件并非只有计算核心,实际的软件是交付给最终用户的软件,除了计算核心外,还需要有一定的界面和必要的辅助功能。
这个Core模块和使用它的其他模块之间则要通过一定的API来交流。
API应该怎么设计呢?
为了方便起见,我们可以从下面的最简单的接口开始(仅举例,你的代码里可能没有这个函数):
int countChar(File *file)
这个函数表示输出一个文件指针,返回这个文件的字符数。
假设我们用Core封装了这个接口,那么我们的测试程序可以是这样:
File *in = fopen("input.txt","r");
int count = 100;
Assert(countChar(in) == count);
当然,这样的测试程序并不充分,希望大家测试时不要像这样偷懒。
3、单元测试和性能分析
请根据自己以往积累的测试经验,在编码完成之后,提交产品之前,设计测试用例,并编写单元测试,对自己的项目进行测试。首先,至少应采用白盒测试用例设计方法来设计测试用例,其他测试方法不限。其次,要设计至少10个测试用例,确保你的程序能够正确处理各种情况。最后,结合测试评估的要求,对自己的测试设计进行评价,这些测试用例能满足该程序测试的要求吗?
另一个重要的措施是要把单元测试自动化,这样每个人都能很容易地运行它,并且可以使单元测试每天都运行。每个人都可以随时在自己的机器上运行。团队一般是在每日构建中运行单元测试的,这样每个单元测试的错误就能及时被发现并得到修改。
请阅读邹欣老师的博客,编写程序的单元测试:关于单元测试和回归测试
c++可以使用vs2017。对于博客任务中的单元测试、性能分析(Studio Profiling Tools),vs2017有相应的功能。
java可以使用idea。也可以自行查找使用其他工具。
package statistics;
import java.io.;
import java.util.;
/**题目:用java,统计txt文件中每个文字或者字母出现的次数,并由大到小排列,
并显示每个文字或者字母出现的次数
思路:
1.定义字符读取(缓冲)流
2.循环读取文件里的字符,用一个String类型变量接收(newValue)
3.把newValue变成字符数组 char[] ch = newValue.toCharArray();
4.遍历ch 将ch中所有的字符存入一个Map集合中(TreeSet),键对应字符,值对应字符出现的次数
5.遍历打印map集合中的键和值,也就是字符出现的次数
package statistics;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class FileWordCount {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("D:\test.txt"));
String s;
StringBuffer sb = new StringBuffer();
while ((s = br.readLine()) != null) {
sb.append(s);
}
Map<String,Integer> map = new HashMap<String, Integer>();
StringTokenizer st = new StringTokenizer(sb.toString(),",.!
");
while (st.hasMoreTokens()) {
String letter = st.nextToken();
int count;
if (map.get(letter) == null) {
count = 1;
} else {
count = map.get(letter).intValue() + 1;
}
map.put(letter,count);
}
Set<WordEntity> set = new TreeSet<WordEntity>();
for (String key : map.keySet()) {
set.add(new WordEntity(key,map.get(key)));
}
// 自己拼接字符串,输出我们想要的字符串格式
System.out.println("输出形式一:");
for (Iterator<WordEntity> it = set.iterator(); it.hasNext(); ) {
WordEntity w = it.next();
System.out.println("单词:" + w.getKey() + " 出现的次数为: " + w.getCount());
}
// 直接打印 WordEntity 对象,实现我们想要的输出效果,只需在WordEntity类中重写toString()方法
System.out.println("输出形式二:");
for (Iterator<WordEntity> it = set.iterator(); it.hasNext(); ) {
WordEntity w = it.next();
System.out.println(w);
}
// 我们可以控制只输出前三名来
System.out.println("输出形式三:");
int count = 1;
for (Iterator<WordEntity> it = set.iterator(); it.hasNext(); ) {
WordEntity w = it.next();
System.out.println("第" + count + "名为单词:" + w.getKey() + " 出现的次数为: "
+ w.getCount());
if (count == 3)// 当输出3个后跳出循环
break;
count++;
}
} catch (FileNotFoundException e) {
System.out.println("文件未找到~!");
} catch (IOException e) {
System.out.println("文件读异常~!");
}
}
}
package statistics;
public class WordEntity implements Comparable<WordEntity> {
private String key;
private Integer count;
public WordEntity (String key, Integer count) {
this.key = key;
this.count = count;
}
public int compareTo(WordEntity o) {
int cmp = count.intValue() - o.count.intValue();
return (cmp == 0 ? key.compareTo(o.key) : -cmp);
//只需在这儿加一个负号就可以决定是升序还是降序排列 -cmp降序排列,cmp升序排列
//因为TreeSet会调用WorkForMap的compareTo方法来决定自己的排序
}
@Override
public String toString() {
return key + " 出现的次数为:" + count;
}
public String getKey() {
return key;
}
五、测试