博客地址: :https://github.com/HandsomeLuoYM/WC
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 4-5天 | 5天 |
-Estimate | 估计这个任务需要多少时间 | 4-5天 | 5天 |
Development | 开发 | 385 | 360 |
-Analysis | 需求分析 (包括学习新技术) | 60 | 50 |
-Design Spec | 生成设计文档 | 30 | 20 |
-Design Review | 设计复审 (和同事审核设计文档) | 15 | 10 |
-Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
-Design | 具体设计 | 20 | 25 |
-Coding | 具体编码 | 200 | 180 |
-Code Review | 代码复审 | 30 | 35 |
Test | 测试(自我测试,修改代码,提交修改) | 200 | 220 |
-Reporting | 报告 | 120 | 150 |
-Test Report | 测试报告 | 60 | 50 |
-Size Measurement | 计算工作量 | 10 | 5 |
-Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 10 | 15 |
合计 | 585 | 580 |
解题思路
- 首先确定编程语言,由于之前C语言未学到操作文件的相关操作,所以选择了比较熟悉的 java 语言 ,降低学习成本。
- 其次,确定可能给使用到的技术,由于需要操作文件,所以需要用到IO流和文件操作相关知识。然后涉及到大量的判断操作,所以打算用正则匹配相关方法。最后加上日志相关操作。
- 最后,对项目代码需要用到GitHub远程仓库进行代理。
设计实现过程
- 首先,确定基本架构,由于只是一个简单的小程序,所以用普通的项目结构即可,也不需要拆分成太多类。
- 其次就是设计方法,首先根据指令长度,拆分成简单操作方法和递归操作方法,而简单方法中含有拓展相关操作,故还需一个拓展的方法。
- 最后就是建立一个bat文件,方便最后测试后面的环节。
代码说明
- 正则表达式以及日志输出等数据的声明
//拆分单词的正则表达式
private static final String SPILT_LINE_TO_WORD_REGEX = "\s+|\(|\)|,|\.|\:|\{|\}|\-|\+|;|\?|\/|\\|/";
//匹配单词的正则表达式
private static final String WORD_REGEX = "[\w]+";
//判断单行注释的正则表达式
private static final String SINGLE_EXPLAIN_REGEX = "\s*(\{|\})?//.*";
//判断多行注释行
private static final String MULTIPLE_ROWS_EXPLAIN_REGEX_HEAD = "\s*/\*.*";
private static final String MULTIPLE_ROWS_EXPLAIN_REGEX_TAIL = ".*\*/";
//空白行正则表达式
private static final String BLANK_CODE_REGEX = "(\{|\})?\s*";
//文件路径格式正则表达式
private static final String FILE_REGEX = "[a-zA-Z]:(\\([a-zA-Z0-9_]+.[a-zA-Z0-9_]{1,16}))+";
//错误日志输出
private static final Logger log = Logger.getLogger("Wc");
//简单封装日志输出信息,防止多次new对象,在此可用枚举进一步封装
private static final Map<String,String> LOG_MAP = new HashMap<String, String>();
static {
LOG_MAP.put("pathError","Error:文件路径 错误 或 不存在!");
LOG_MAP.put("operationError","Error:输入的指令错误!");
LOG_MAP.put("invalidError","Error:无效指令!");
}
- Main方法
/**
* 主类
* @param args 输入参数
* @throws IOException IO流异常,抛出,不做处理
*/
@SuppressWarnings("unused")
public static void main(String[] args) throws IOException {
System.out.println("---------------------------------------------------------");
System.out.println("| 程序功能 : |");
System.out.println("|-c:返回文件的字段数 |");
System.out.println("|-w:返回文件的词的数 |");
System.out.println("|-l:返回文件的行数 |");
System.out.println("|-s:递归处理目录下符合条件的文件 |");
System.out.println("|-a:返回文件的代码行数/空白行数/注释行数 |");
System.out.println("---------------------------------------------------------");
//获取用户输入指令
Scanner scanner = new Scanner(System.in);
//循环来获取用户操作
while (true){
System.out.println("---------------------------------------------------------");
System.out.println("请输入命令(格式:wc.exe [parameter] [file_path]):");
//将指令集拆分,并做简单判断格式
String[] split = scanner.nextLine().split(" ");
if("wc.exe".equals(split[0]) && 3 == split.length){
basicOperation(split);
}else if("wc.exe".equals(split[0]) && 4 == split.length){
if (split[3].matches(FILE_REGEX) && new File(split[3]).exists()){
recursionOperation(split);
}else {
log.severe(LOG_MAP.get("pathError"));
}
}else {
log.severe(LOG_MAP.get("operationError"));
}
}
}
- 用户指令基本操作类
/**
* 判定用户操作
* @param split 指令拆分集
* @throws IOException IO流异常抛出
*/
@SuppressWarnings("unused")
public static void basicOperation(String[] split) throws IOException {
//初始化统计数据参数
int characterCount = 0, lineCount = 0, wordCount = 0;
//进一步判断输入文件路径是否正确
File file = new File(split[2]);
String fileName = file.getName();
if (!split[2].matches(FILE_REGEX) || !file.exists() || file.isDirectory()
||!"c".equals(fileName.substring(fileName.lastIndexOf(".")+1,fileName.length()))){
log.severe(LOG_MAP.get("pathError"));
return;
}
//获取本地文件的缓存流读取器,方便后面操作
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(split[2])));
//用于暂时存储行数据
String line;
switch (split[1]){
case "-c" :
while (null != (line = br.readLine())){
characterCount += line.length();
}
System.out.println("字符数为:" + characterCount);
break;
case "-l" :
while (br.readLine() != null){
lineCount++;
}
System.out.println("行数为:" + lineCount);
break;
case "-w" :
while ((line = br.readLine()) != null){
for (String s : line.split(SPILT_LINE_TO_WORD_REGEX)){
if (s.matches(WORD_REGEX)){
wordCount ++;
}
}
}
System.out.println("单词数:"+wordCount);
break;
case "-a" :
specialOperation(br);
break;
default:
log.warning(LOG_MAP.get("invalidError"));
}
br.close();
}
- 特殊操作
/**
* 统计一些高级的数据
* @param bufferedReader 缓存流读取器
* @exception IOException IO流异常,直接抛出,不做处理
*/
@SuppressWarnings("unused")
public static void specialOperation(BufferedReader bufferedReader) throws IOException {
//初始化统计数据参数
int blankCount = 0, codeCount = 0, explainCount = 0;
String line;
boolean b = false;
while (null != (line = bufferedReader.readLine())){
if (line.matches(BLANK_CODE_REGEX)){
blankCount++;
}else if (line.matches(SINGLE_EXPLAIN_REGEX)){
explainCount++;
}else if (line.matches(MULTIPLE_ROWS_EXPLAIN_REGEX_HEAD)){
explainCount++;
if (!line.matches(MULTIPLE_ROWS_EXPLAIN_REGEX_TAIL)){
b = true;
}
}else if (b){
explainCount++;
if (line.matches(MULTIPLE_ROWS_EXPLAIN_REGEX_TAIL)){
b = false;
}
}else {
codeCount++;
}
}
System.out.println("代码行数:" + codeCount);
System.out.println("注释行数:" + explainCount);
System.out.println("空白行数:" + blankCount);
bufferedReader.close();
}
- 递归操作
/**
* 递归判断文件
* @param split 递归的指令集
* @exception IOException 直接抛出,不做处理
*/
@SuppressWarnings("unused")
public static void recursionOperation(String[] split) throws IOException {
File file = new File(split[3]);
if (file.isDirectory()){
//判断为文件
File[] files = file.listFiles();
for(File f :files){
split[3] = f.getPath();
recursionOperation(split);
}
}else {
//判断为非文件
String fileName = file.getName();
//判断是否为C后缀文件
if ("c".equals(fileName.substring(fileName.lastIndexOf(".")+1,fileName.length()))){
String[] operation = new String[]{split[0],split[2],file.getPath()};
System.out.println(file.getPath());
basicOperation(operation);
System.out.println("-------------------------------------------------------------");
}
}
}
- bat文件:
javac -encoding utf-8 Wc.java
java Wc
测试运行
- 测试 -c 命令
- 测试 -w 命令
- 测试 -l 命令
- 测试 -a 命令
- 测试 -s 命令
test文件目录结构
test00.c
test01.c
test02.c
测试结果:
一些错误的指令输入
项目小结
-
优点:有了充足的规划后,在进度方面有了较为合理的时间安排。也了解了软件开发的一些相关知识。
-
缺点:时间不充足,很多功能未能来得及实现
-
改进:我认为在之后的开发中,规划可能可以适当的延长,将开发环节效率最高化。