GitHub
- GitHub项目地址:https://github.com/LYX708194/WordCout
一、项目简介
项目要求
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:
wc.exe [parameter] [file_name]
功能列表
基本功能
-c file.c //返回文件 file.c 的字符数
-w file.c //返回文件 file.c 的词的数目
-l file.c //返回文件 file.c 的行数
扩展功能
-s 递归处理目录下符合条件的文件。
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 40 | 40 |
· Estimate | · 估计这个任务需要多少时间 | 500 | 655 |
Development | 开发 | 430 | 490 |
· Analysis | · 需求分析 (包括学习新技术) | 100 | 120 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 20 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 20 | 25 |
· Coding | · 具体编码 | 200 | 200 |
· Code Review | · 代码复审 | 20 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 25 |
Reporting | 报告 | 40 | 55 |
· Test Report | · 测试报告 | 15 | 20 |
· Size Measurement | · 计算工作量 | 10 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 15 | 20 |
合计 | 510 | 655 |
三、思路
因为之前学过Java,当即决定了实现项目所要使用的语言。因为要实现读取文件的操作,加之之前对文件io之类的使用较少,故重新去熟悉了一下Java的io流。
先从实现项目的基本功能开始,一步一步慢慢实现。其本质就是对文件的读取内容进行解析。
四、设计实现过程
根据需求对功能进行分割,一共有五个功能,分别为:
- -c 获取字符数
- -w 获取单词数
- -l 获取文件行数
- -a 获取文件空行,注释行,代码行三种数据
- -s 递归文件目录的操作,后面需接上面四类操作数
故将其分割为五个方法,每种方法进行一种操作。
项目共有四个类,分别为:
- Main------------------------主程序入口
- GetFileInputStream----------对文件的io操作
- WC---------------------------wc实体类,记录各个数据
- WordCount-------------------各种获取方法的功能就在这个类里面
执行流程如下:
五、代码说明
文件读取操作
- 使用缓冲流BufferedReader来读取文件效率更好
public static BufferedReader getFileInputStream(String fileName) {
BufferedReader bufferedReader = null;
File file = new File(fileName);
if (file.exists()){
try {
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("该路径文件不存在!");
}
}else{
System.out.println("该路径或文件不存在!");
return null;
}
return bufferedReader;
}
获得文件单词数------ -w 操作
/**
* 获得单词数------ -w 操作
* @param fileName 操作文件名
* @return 返回单词数
*/
public static int getWordCount(String fileName){
int count1 = 0;
BufferedReader bufferedReader01 = GetFileInputStream.getFileInputStream(fileName);
if(bufferedReader01 == null){
return -1;
}
try {
String str = null;
//获得每行的数据,将这一行数据分隔开存入数组中,每行数组个数相加即为单词个数
while((str=bufferedReader01.readLine())!=null){
String oneLine[] = str.split("\s");
count1+=oneLine.length;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bufferedReader01.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return count1;
}
获得文件字符数----- -c 操作
/**
* 获得字符数----- -c 操作
* @param fileName 操作文件名
* @return 返回字符数
*/
public static int getCharCount(String fileName){
int count2 = 0;
BufferedReader bufferedReader02 = GetFileInputStream.getFileInputStream(fileName);
if(bufferedReader02 == null){
return -1;
}
String str;
try {
//循环读取字符,直到流结束
while((str = bufferedReader02.readLine())!=null){
//每次读取一行后将字符串转化成字符数组,每次将字符数组的值相加即得字符的总数
char c[] = str.toCharArray();
count2+=c.length;
}
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭流
bufferedReader02.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return count2;
}
获得文件行数----- -l 操作
/**
* 获得行数----- -l 操作
* @param fileName 操作文件名
* @return 返回行数
*/
public static int getLineCount(String fileName){
int count3 = 0;
BufferedReader bufferedReader03 = GetFileInputStream.getFileInputStream(fileName);
if(bufferedReader03 == null){
return -1;
}
try {
//是否有下一行
while(bufferedReader03.readLine()!=null) {
count3++;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭流
bufferedReader03.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return count3;
}
获得空行、注释行、代码行----- -a 操作
/**
* 获得空行、注释行、代码行----- -a 操作
* @param fileName 操作文件名
* @return 返回wc对象,对象中包含三个复杂数据
*/
public static WC getThreeCount(String fileName){
BufferedReader br = GetFileInputStream.getFileInputStream(fileName);
WC wc = new WC();
int spaceCount = 0;
int nodeCount = 0;
int codeCount = 0;
//判断是否为多行注释的标志
boolean flag = false;
if(br==null){
return null;
}
try {
String s1 = null;
String s2 = null;
while((s1=br.readLine())!=null){
if (flag){
if (s1.endsWith("*/")){
flag = false;
}
nodeCount++;//注释行+1
}else {
s2 = s1.replaceAll("\s","");
if ("".equals(s2)){
spaceCount++;
}else if (s2.startsWith("//")||s2.startsWith("}//")){
nodeCount++;
}else if (s2.startsWith("/*")){
if (!s2.endsWith("*/")){
//不是单行注释,标志置为真
flag = true;
}
nodeCount++;
}else {
codeCount++;
}
}
}
wc.setNodeCount(nodeCount);
wc.setSpaceCount(spaceCount);
wc.setCodeCount(codeCount);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return wc;
}
遍历文件夹下所有目录,包括子目录
/**
* 遍历文件夹下所有目录,包括子目录
* @param path 路径
* @param fileList 文件列表
*/
public static void getFileList(String path,List<String> fileList){
File file = new File(path);
if (file.exists()){
if (file.isDirectory()){
File[] files = file.listFiles();
if (null == files||files.length == 0){
System.out.println("此目录为空!");
return ;
}else {
for (File f:files) {
if (f.isDirectory()){
getFileList(f.getAbsolutePath(),fileList);
}else {
fileList.add(f.getAbsolutePath());
}
}
}
}else{
System.out.println("这个不是目录路径!");
}
}else {
System.out.println("此路径不存在!");
}
}
六、测试运行
空文件
单个字符
一个单词
一行多个单词
多行文件
代码文件(含注释)
递归目录
错误输入
七、总结
- 实际花费时间比预计时间多了很多,主要是在对以前学过的一些知识点没那么熟悉,需要回去查询资料加深认识
- 熟悉了进行一个个人项目所需要的流程和步骤,明白了要做好计划,跟着计划来执行效率会更好
- 一些功能还没有做好,比如文件通配符和图形化界面,后续如果有时间的话会继续去完善它
- 对功能的需求要考虑好后续的使用和迭代,另外要注意代码简洁性