Github项目地址:https://github.com/czmDeRepository/SoftwareWork/tree/master/work
一、题目描述
- 实现一个简单而完整的软件工具(源程序特征统计程序)。
- 进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
- 进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。
二、WC 项目要求
- wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
- 实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
- 具体功能要求:程序处理用户需求的模式为:wc.exe [parameter] [file_name]
三、核心代码
- 获取文件字符缓存流
private static BufferedReader GetFileInmputStream(String fileName){
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
} catch (FileNotFoundException e) {
System.out.println("系统找不到指定路径文件");
}
return bufferedReader;
}
- 返回文件字符数
public static int CharCount(String fileName){
BufferedReader in = null;
int count = 0;
in = GetFileInmputStream(fileName);
if (in == null){
return ERROR_NUM;
}
int result = ERROR_NUM;
try {
while((result = in.read()) != -1){
if(result != '\r' && result!='\n'){
count++;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
- 返回文件行数
public static int LineCount(String fileName){
BufferedReader read = null;
int count = 0;
read = GetFileInmputStream(fileName);
if (read == null){
return ERROR_NUM;
}
try {
while (read.readLine() != null){
count++;
}
// 速度较前者慢
// Iterator<String> iterator = read.lines().iterator();
// while (iterator.hasNext()){
// iterator.next();
// count++;
// }
} catch (IOException e) {
e.printStackTrace();
}finally {
if(read != null){
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
- 返回文件单词数
public static int WordCount(String fileName){
BufferedReader in = null;
int count = 0;
in = GetFileInmputStream(fileName);
if (in == null){
return ERROR_NUM;
}
try {
String str = null;
while((str = in.readLine()) != null){
//\\s+表示 空格,回车,换行等空白符
String[] split = str.split("\\s+");
count += split.length;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
- 显示(代码行 / 空行 / 注释行)
public static void Complex(String filename){
int codeLine = 0;
int annotationLine = 0;
int blankLine = 0;
// 判断是否处于多行注解内
boolean flag = false;
BufferedReader bufferedReader = GetFileInmputStream(filename);
if (bufferedReader == null){
return;
}
String strLine = null;
String newLine = null;
try {
while ((strLine = bufferedReader.readLine())!= null){
if (flag) {
if (strLine.endsWith("*/")){
flag = false;
}
annotationLine++;
}else {
// 去除空格
newLine = strLine.replaceAll("\\s*", "");
if("".equals(newLine)){
//空行
blankLine++;
}else if (newLine.startsWith("/*")){
//注释行
if (!newLine.endsWith("*/")){
//去除/*单行注释*/情况
flag = true;
}
annotationLine++;
}else if (newLine.startsWith("//") || newLine.startsWith("}//")){
//注释行
annotationLine++;
}else {
//代码行
codeLine++;
}
}
}
System.out.println("代码行:"+codeLine);
System.out.println("空行:"+blankLine);
System.out.println("注释行:"+annotationLine);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 递归处理目录
public static void FolderCount(String operation, String folderName, String fileName){
File file = new File(folderName);
if(file.isDirectory()){
int index = fileName.lastIndexOf('.');
if(index >= 0){
String extension = fileName.substring(index);
String frontName = fileName.substring(0, index);
File[] fileList = file.listFiles();
if (fileList.length > 0){
// String regex = ".*"+name+"\\.+"+extension+"$";
// for (String f:fileList){
// System.out.println(Pattern.matches(regex, f));
// System.out.println(f);
// }
// 递归遍历
if (!ReadFold(operation, file.getName(), fileList, frontName, extension)){
System.out.println("该目录下无对应文件");
}
}else{
System.out.println("该目录为空");
}
}
}else{
System.out.println("参数不是文件夹或路径错误");
}
}
/***
* 递归子文件
* @param operation
* @param fileList
* @param frontName
* @param extension
*/
public static boolean ReadFold(String operation, String parentName, File[] fileList, String frontName, String extension){
boolean flag = false;
boolean childFlag = false;
String fName = null;
for (File f:fileList){
// 子文件是文件
if(f.isFile()){
fName = f.getName();
if (fName.indexOf(frontName) >=0 && fName.endsWith(extension)) {
switch (operation) {
case "-c":
System.out.println("【"+parentName+"】下的【"+fName+"】文件的字符数:" + CharCount(f.getPath()));
break;
case "-w":
System.out.println("【"+parentName+"】下的【"+fName+"】文件的单词数:" + WordCount(f.getPath()));
break;
case "-l":
System.out.println("【"+parentName+"】下的【"+fName+"】文件的行数:" + LineCount(f.getPath()));
break;
case "-a":
System.out.println("【"+parentName+"】下的【"+fName+"】文件的信息:");
Complex(f.getPath());
break;
default:
System.out.println("第二参数错误(-c,-w,-l)");
return false;
}
flag = true;
}
}else{ //子文件是目录
childFlag = ReadFold(operation, f.getName(), f.listFiles(), frontName, extension) || childFlag;
}
}
return (flag || childFlag);
}
- 主函数调用
/**
* 正确传参
*/
private static int FLAG_TWO = 2;
/**
* 出错误返回值
*/
private static int ERROR_NUM = -1;
public static void main(String[] args) {
if(args.length >= FLAG_TWO){
switch (args[0]){
// 字符数
case "-c":
long startC = System.currentTimeMillis();
System.out.println("字符数:"+CharCount(args[1]));
System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startC);
break;
// 单词数
case "-w":
long startW = System.currentTimeMillis();
System.out.println("单词数:"+WordCount(args[1]));
System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startW);
break;
// 行数
case "-l":
long startL = System.currentTimeMillis();
System.out.println("行数:"+LineCount(args[1]));
System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startL);
break;
// 递归处理目录下符合条件的文件。
case "-s":
if(args.length == 4){
long startS = System.currentTimeMillis();
FolderCount(args[1], args[2], args[3]);
System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startS);
}else{
System.out.println("正确格式为:wc.exe -s 【操作】 文件夹路径 文件名");
}
break;
// 返回更复杂的数据(代码行 / 空行 / 注释行)。
case "-a":
long startA = System.currentTimeMillis();
Complex(args[1]);
System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startA);
break;
default:
System.out.println("请输入正确参数【操作】+文件路径");
}
}else {
System.out.println("请输入正确参数【操作】+文件路径");
}
}
四、项目测试
- 用exe4j将jar包转exe程序再测试
五、PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 10 | 15 |
· Estimate | · 估计这个任务需要多少时间 | 200 | 300 |
Development | 开发 | 120 | 200 |
· Analysis | · 需求分析 (包括学习新技术) | 15 | 10 |
· Design Spec | · 生成设计文档 | 10 | 10 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
· Design | · 具体设计 | 30 | 25 |
· Coding | · 具体编码 | 100 | 200 |
· Code Review | · 代码复审 | 10 | 15 |
· Test | · 测试(自我测试,修改代码,提交修改) | 10 | 30 |
Reporting | 报告 | 15 | 20 |
· Test Report | · 测试报告 | 10 | 30 |
· Size Measurement | · 计算工作量 | 15 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 15 | 30 |
合计 | 575 | 920 |
六、总结
实际时间总比预期长,敲代码容易改代码难,思路要清晰,读取文件用缓冲流效果更好,将jar包转exe再执行程序感觉比直接运行慢,” || “判断只要前者为真后者就不再执行!!!学会了简单使用git管理项目。