系统分析与设计结对项目 ——WordCount
合作者:201631105117(同学一),201631062607(同学二)
码云上的项目地址:https://gitee.com/SenLinJ/wc
本次作业的链接地址:https://www.cnblogs.com/cpp-cpp/p/9762389.html
结对的另一个同学的作业地址:https://www.cnblogs.com/silvercv/p/9799229.html
一、结对的PSP表格
PSP2.1表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
20 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
20 |
30 |
Development |
开发 |
670 |
1155 |
· Analysis |
· 需求分析 (包括学习新技术) |
120 |
100 |
· Design Spec |
· 生成设计文档 |
0 |
0 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
0 |
0 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
30 |
· Design |
· 具体设计 |
50 |
115 |
· Coding |
· 具体编码 |
300 |
660 |
· Code Review |
· 代码复审 |
60 |
100 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
120 |
150 |
Reporting |
报告 |
120 |
200 |
· Test Report |
· 测试报告 |
60 |
90 |
· Size Measurement |
· 计算工作量 |
30 |
50 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
60 |
合计 |
810 |
1385 |
二、互审代码情况
第一次的两个WordCount项目的地址:同学一: https://www.cnblogs.com/silvercv/p/9692483.html
同学二: https://www.cnblogs.com/cpp-cpp/p/9695903.html
审查的模块名称、发现的问题等:
同学一:同学二的代码没有采用模块化的形式,逻辑比较严谨,代码风格不错,这点值得学习。
同学二:同学的代码采用了工程式的思想完成,思路清晰,结构完整,注释和命名方面也做的很好。反观自己,就像写个小程序的样子,小惭愧。而且通过看同学的代码,发现了自己代码之前不够严谨的地方。
三、设计过程
这次编程我们决定采用java,我们两个人第一次WordCount都是使用C语言编写的。起初合并时还是使用C语言编写,虽然已经实现那些基本和扩展功能,但是过程太过复杂。虽然我们前期花了一定时间去编写并且大部分功能已经实现,但是高级功能实现不太现实,所以改变了使用的语言,这是我们浪费了许多时间,是我们的失误。
使用结构设计,比如有几个类或几个函数,他们之间关系如何:
我们分类编写,每个类实现一个小功能。比如:
AbstractBaseCount、BaseCount、ExternCount等,类与类之间大多是继承关系。
关键函数的算法设计(流程图):
字符统计:
行数统计:
如果有停用词,按照获取单词的流程可以获取一个停用词的Vector,然后通过循环,来遍历停用词和从要处理的文件中或取的单词列表,
记录单词列表中出现停用词的次数,然后用总的单词次数减去停用次的次数,那么就可得到去除停用词之后的单词数量。
以尾递归的形式将所有文件放入到一个文件列表中,对于这个文件列表遍历执行所需要进行的操作。
四、代码说明
关键代码及注释说明:
protected BufferedReader bufferedReader;
protected BufferedWriter bufferedWriter;
public AbstractBaseCount(){
}
public AbstractBaseCount(File fileName){
getBufferedInputStream(fileName);
}
public AbstractBaseCount(File input_file, String output_file){
getBufferedInputStream(input_file);
this.output = output_file;
}
try{
File file = new File(filePath);
bufferedWriter = new BufferedWriter(new FileWriter(file,flag));
}catch (Exception e){
System.out.println(e.toString());
}
}
private void getBufferedInputStream(File file){
try{
if(!file.exists()){
Util.Tips();
}
bufferedReader = new BufferedReader(new FileReader(file));
}catch (Exception e){
System.out.println(e.toString());
}
}
public void Write(List<String> buff){
/*
buff 为写入文件的字符
flag 表示其模式
*/
getBufferedWriter(output,false);
boolean flag = false;
if(buff.size() >= 2){
flag = true;
}
getBufferedWriter(output,flag);
try{
for(String str: buff) {
bufferedWriter.write(str);
}
}catch (IOException e){
System.out.println(e.toString());
}finally {
try{
bufferedWriter.close();
}catch (IOException e){
System.out.println(e.toString());
}
}
}
protected String filename;
protected List<String> buff = new ArrayList<>(); //将读取的东西保存在内存中,不用反复读取
private void GetBUff(){
String buf;
try {
while ((buf = bufferedReader.readLine()) != null) {
buff.add(buf);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try{
bufferedReader.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
public BaseCount(){
super();
GetBUff(); //读取数据到文件中
}
public BaseCount(File file){
super(file);
this.filename = file.getName();
GetBUff();
}
public BaseCount(File inputFile, String output_file){
super(inputFile,output_file);
this.filename = inputFile.getName(); //输出文件
GetBUff();
}
protected int getCharNum(){ //获取字符的数量
int CharNum = 0;
for(String str : buff){
CharNum += str.length() ; //获取每一行字符的数量
}
return CharNum;
}
int WordNum = 0;
for(String str:buff){
WordNum += str.replace(" ",",").split(",").length;//获取单词的数量
}
return WordNum;
}
int LineNum = 0;
LineNum = buff.size() ;//行数
return LineNum;
}
/*这个类是用来实现扩展功能 * */ public class ExternCount extends BaseCount { public ExternCount(){ super(); } public ExternCount(File fileName){ super(fileName); } public ExternCount(File inputFile, String output_file){ super(inputFile,output_file); } protected String OtherLines(){ int CodeLine = 0; //代码行 int NoteLine = 0; //注释行 int SpacLine = 0; //空行 int size = buff.size(); for (int i=0; i<size;i++){ if (buff.get(i).length() <= 1 || buff.get(i).trim().length() <=1){ SpacLine++; //当整行中只有少于一个可显示字符的时候 }else{ if (buff.get(i).contains("/*")){ while(!buff.get(i).contains("*/")){ i++; NoteLine++; //用来判断块注释 } i++; NoteLine++; }else{ String[] sp = buff.get(i).split("//"); //用注释符分开 if(sp.length <= 1 && buff.get(i).contains("//")){ //本行含有注释符 NoteLine++; }else if (sp[0].length() <=1){ //在注释符前含有一个字符 NoteLine++; }else { CodeLine++; //其余情况为代码行 } } } } //最终返回一个字符串 String buf = filename + ", 代码行/注释行/空行:"+CodeLine+"/"+NoteLine+"/"+SpacLine+" "; return buf; } protected int StopSize(String StopWordsFile){ int Stopsize = 0; try { List<String> StopWords = getStopWords(StopWordsFile);//加载停用词 List<String> Words = getWords(); for(String stop : StopWords){ if(Words.remove(stop)){ Stopsize++; } } }catch (Exception e){ System.out.println(e.toString()); } return Stopsize; } //从文件中读取停用词 private List<String> getStopWords(String StopWordsFile){ List<String> StopWords = new ArrayList<>(); try{ String buf = null; File file = new File(StopWordsFile); if(!file.exists()){ Util.Tips(); }else { BufferedReader buffered = new BufferedReader(new FileReader(StopWordsFile)); //包含停用词的文件 while ((buf = buffered.readLine()) != null) { //停用词以空格分开 for (String s : buf.split(" ")) { StopWords.add(s); } } } }catch (Exception e){ System.out.println(e.toString()); } return StopWords; } private List<String> getWords(){ //获取所有的单词 List<String> Words = new ArrayList<>(); try{ for(String buf:buff){ for(String s : buf.replace(' ',',').split(",")){ Words.add(s); } } }catch (Exception e){ System.out.println(e.toString()); } return Words; } }
//解析命令行参数 public class ParseArgs { private static String ext = "."; //从命令行中获取文件和参数 public static Map<String,String> getOptions(String[] args) { if (args.length < 1){ Util.Tips(); } HashMap<String, String> options = new HashMap<>(); int size = args.length; //判断当只有一个参数的时候 if (size == 1 && !args[size-1].equals("-x")){ Util.Tips(); }else if(args[size-1].equals("-x")) { options.put("-x","-x"); } if(size >= 2) { // -o或者-e后没有文件 if (args[size - 1].equals("-o") || args[size - 1].equals("-e")) { Util.Tips(); } if (args[size - 2].equals("-o") && !args[size - 1].equals("-o")) { options.put(args[size - 2], args[size - 1]); size -= 2; } else if (args[size - 3].contains("-")) { Util.Tips(); } for (int i = 0; i < size; i++) { //停用词必须在-e之后 if (args[i].equals("-e") && args[i + 1].endsWith(".txt")) { options.put(args[i], args[++i]); } else if (args[i].equals("-e") && !args[i + 1].endsWith(".txt")) { Util.Tips(); } else if (args[i].equals("-e") && args[i - 1].contains("-")) { Util.Tips(); } else { options.put(args[i], args[i]); //将参数放入哈希表中 } } } return options; //返回一个哈希map } private static String getDir(String FileName){ String dir = null; String[] exts = FileName.split("\."); if(!exts[0].endsWith("*") ){ //如果只是一个文件 或者是一个文件夹 return FileName; } else { if(exts[0].endsWith("*") && exts[0].equals("*")) { dir = new File("").getAbsolutePath(); //获取绝对路径 }else{ dir = exts[0].split("\*")[0]; } } return dir; } private static void getAllFiles(String FileName,List<File> Files){ try{ String dir = getDir(FileName); //获取文件路径 File file = new File(dir); //获取当前文件路径下的所有文件或者路径 if (file != null &&file.isFile()){ Files.add(file); }else { File[] files = file.listFiles(); if (files != null && files.length !=0 ){ for (File f : files) { if (f.isDirectory()) { getAllFiles(f.getAbsolutePath(), Files); } else if(f.isFile()){ if(ext == null){ Files.add(f); }else{ if(f.getName().endsWith(ext)){ Files.add(f); } } } } } } }catch (Exception e){ System.out.println(e.toString()); } } public static List<File> getAllFiles(String FileName){ String[] exts = FileName.split("\."); //如果没有扩展名 if (exts.length == 1){ ext = null; }else{ ext += exts[exts.length-1]; } List<File> files = new ArrayList<>(); getAllFiles(FileName,files); if (files == null || files.size() == 0){ Util.Tips(); } return files; } public static void Start(String[] args) { Map<String,String> options = getOptions(args); //如果参数中含有-x if (options.containsKey("-x")){ WordsUI.main(args); }else{ String fileName = null; //找到文件位置 for(String key : options.keySet()){ if (!key.contains("-")){ fileName = options.get(key); //处理读入文件 break; } } if (fileName == null){ Util.Tips(); } String output = null; List<String> buf = new ArrayList<>(); List<File> files = new ArrayList<>(); //有-s参数 if (options.containsKey("-s")) { files = getAllFiles(fileName); }else{ //无递归调用条件 try{ files.add(new File(fileName)); }catch (Exception e){ System.out.println(e.toString()); } } if (options.containsKey("-o")){ output = options.get("-o"); //获取输出文件的位置 }else{ output = null; } WordCount wc; for (File f: files){ if (null != output){ wc = new WordCount(f,output); }else { wc = new WordCount(f); } if (options.containsKey("-c")){ buf.add(wc.CharSize()); } if (options.containsKey("-w") && !options.containsKey("-e")){ buf.add(wc.WordSize()); } if (options.containsKey("-l")){ buf.add(wc.LineSize()); } if (options.containsKey("-a")){ buf.add(wc.OtherLine()); } if (options.containsKey("-w") && options.containsKey("-e")){ buf.add(wc.WordSize(options.get("-e"))); } wc.Write(buf); } } } }
五、总结
结合在构建之法中学习到的相关内容与结对项目的实践经历,描述结对的感受,是否1+1>2?
同学一:见他的博客:https://www.cnblogs.com/silvercv/p/9799229.html
同学二:我感受到了1+1>2 。我们分别都充当了驾驶员和领航员的角色。对于模块,我们一起讨论,得出解决方案,然后我在编程时,同学在一旁指导,指出错误。
同学在编程时,我也可以观看他平时的一些写代码的习惯,借鉴一些值得学习的地方。比如会在代码开头给一个注释,解释一些关键变量和关键函数等。
所以,我认为结对编程使代码的质量都提升了,也让大家相互学习,得到了发展。
这次项目虽然完成了,但是时间远远超过了预期时间,中途更换编程语言是一个方面,另一个方面是处理.c文件那部分,对功能的分析花费的时间太长了。好在还是做出来啦。
最后,当然是感谢我的小伙伴啦~在他的帮助下,我们一起完成了这个小项目。从他那里学到了很多东西。