WordCount --java语言实现
由于GitHub网速问题(我在医院,没有wifi,只能开热点,上github速度过慢,难以将项目push到github上,因此,我的项目托管到了码云上)
gitee地址:!()[https://gitee.com/wangyuyong123/wordcloud]
1. 项目要求
1.1 题目描述
Word Count
实现一个简单而完整的软件工具(源程序特征统计程序)。
进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。
1.2 功能列表
基本功能列表:
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的词的数目
wc.exe -l file.c //返回文件 file.c 的行数
扩展功能:
-s 递归处理目录下符合条件的文件。
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释
在这种情况下,这一行属于注释行。
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
需求举例:
wc.exe -s -a *.c
返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。
2. PSP
PSP2.1
Personal Software Process Stages
预估耗时(分钟)
实际耗时(分钟)
Planning
计划
20
20
· Estimate
· 估计这个任务需要多少时间
350
312
Development
开发
290
252
· Analysis
· 需求分析 (包括学习新技术)
80
60
· Design Spec
· 生成设计文档
30
20
· Design Review
· 设计复审 (和同事审核设计文档)
20
15
· Coding Standard
· 代码规范 (为目前的开发制定合适的规范)
20
20
· Design
· 具体设计
30
32
· Coding
· 具体编码
50
55
· Code Review
· 代码复审
30
25
· Test
· 测试(自我测试,修改代码,提交修改)
30
25
Reporting
报告
60
60
· Test Report
· 测试报告
20
20
· Size Measurement
· 计算工作量
20
20
· Postmortem & Process Improvement Plan
· 事后总结, 并提出过程改进计划
20
20
合计
370
332
3.关键代码与设计说明
3.1 总体设计
读入文件模块为ReadFileCls类实现
计数模块为WordCount类实现
程序启动后,程序将根据用户输入的参数判断是否合法,如果合法,将调用ReadFileCls类读入相应的文件,将文件存入内存后,只需要调用ReadFileCls的getContent方法即可获取文件内容的字符串,接着将文件内容的字符串传入WordCount类构成新的对象,根据用户输入的不同参数,调用wordcount不同的方法可以获得相应的结果(如,用户输入-c,则调用wordcount对象的getContentLength方法获取文件的字符数量)。
主函数的main方法如下:
import java.util.List;
public class Main {
public static ReadFileCls read;
public static WordCount wordCount;
public static void main(String[] args) {
if (args.length >= 3) {
int i = 0;
if (args[i].equals("-a") || args[++i].equals("-a")) {
int j = 1 - i;
if (args[j].equals("-c")) {
try {
operateAllFile(args[2], new Do() {
@Override
public void doSomething(String fileName, WordCount wordCount) {
System.out.println(fileName + "的字符数为:" + wordCount.getContentLength());
}
});
}catch (Exception e) {
showNoFile();
}
}else if (args[j].equals("-w")) {
try {
operateAllFile(args[3], new Do() {
@Override
public void doSomething(String fileName, WordCount wordCount) {
System.out.println(fileName + "的单词数为:" + wordCount.getWordCount());
}
});
}catch (Exception e) {
showNoFile();
}
}else if (args[j].equals("-l")) {
try {
operateAllFile(args[2], new Do() {
@Override
public void doSomething(String fileName, WordCount wordCount) {
System.out.println(fileName + "的行数为:" + wordCount.getContentLine());
}
});
}catch (Exception e) {
showNoFile();
}
}else {
showError();
}
}else {
showError();
}
} else if (args.length >= 2) {
if (args[0].equals("-c")) {
try {
init(args[1]);
System.out.println(args[1] + "字符数为:" + wordCount.getContentLength());
}catch (Exception e) {
showNoFile();
}
}else if (args[0].equals("-w")) {
try {
init(args[1]);
System.out.println(args[1] + "单词数:" + wordCount.getWordCount());
}catch (Exception e) {
showNoFile();
}
}else if (args[0].equals("-l")) {
try {
init(args[1]);
System.out.println(args[1] + "行数:" + wordCount.getContentLine());
}catch (Exception e) {
showNoFile();
}
}else {
showError();
}
} else {
showError();
}
}
public static void init(String filePath) throws Exception{
read = new ReadFileCls(filePath);
wordCount = new WordCount(read.getContent());
}
public static void showError() {
System.out.println("参数不规范");
}
public static void showNoFile() {
System.out.println("没有找到该文件");
}
public static void operateAllFile(String directory, Do operator)throws Exception{
read = new ReadFileCls(directory,true);
List fileContents = read.getAllContent();
List fileName = read.getAllFileName();
for (int i = 0; i < fileContents.size(); i++) {
wordCount = new WordCount(fileContents.get(i));
if (operator != null) {
operator.doSomething(fileName.get(i),wordCount);
}
}
}
}
3.2 ReadFileCls类的设计
该类有两个构造方法,第一个构造方法只带一个参数,传入文件名,之后调用getContent即可获取该文件内容的字符串(String),该类会将此字符串保留起来,如果下次再次调用,则直接将该字符串返回,无需在读取文件。第二个构造方法带两个参数,第一个是一个文件夹名称,第二个是布尔变量,表示是否递归处理该文件夹,使用该构造函数构造对象后,调用getAllContent方法,可以获取该目录夹下所有文件的内容,并返回一个字符串数组。
该类中,对文件的读写操作流程如下:
该类具体代码如下:
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ReadFileCls {
private File file;
private boolean isAll;
private String content;
public ReadFileCls(String filePath) {
this.file = new File(filePath);
}
public ReadFileCls(String filePath, boolean isAll) {
this.file = new File(filePath);
this.isAll = isAll;
}
/**
* 如果是文件夹,则递归获取文件夹内的所有内容
* 如果是文件,则获取该文件的内容
* @return 字符串数组
* @throws Exception 若该文件不存在,则抛出异常。
*/
public List getAllContent() throws Exception{
List allContent = new ArrayList<>();
if (isAll) {
if (file.exists()) {
if (file.isFile()) {
allContent.add(getContent());
}else {
File[] fileSon = file.listFiles();
if (fileSon != null) {
for (int i = 0; i < fileSon.length; i++) {
List temp = new ReadFileCls(fileSon[i].getAbsolutePath(), true).getAllContent();
allContent.addAll(temp);
}
}
}
}else {
throw new FileNotFoundException("没有此文件");
}
}else {
allContent.add(getContent());
}
return allContent;
}
/**
* 如果是文件夹,则递归获取文件夹内的文件的名字
* 如果是文件,则获取该文件的名字
* @return 字符串数组
* @throws Exception 若该文件不存在,则抛出异常。
*/
public List getAllFileName() throws Exception{
List allFileName = new ArrayList<>();
if (isAll) {
if (file.exists()) {
if (file.isFile()) {
allFileName.add(file.getName());
}else {
File[] fileSon = file.listFiles();
if (fileSon != null) {
for (int i = 0; i < fileSon.length; i++) {
List temp = new ReadFileCls(fileSon[i].getAbsolutePath(), true).getAllFileName();
allFileName.addAll(temp);
}
}
}
}else {
throw new FileNotFoundException("没有此文件");
}
}else {
allFileName.add(file.getName());
}
return allFileName;
}
/**
* 获取文件内容
* @return String 文件内容(若返回值为null,说明文件中没有内容)
* @throws Exception 若该文件不存在,则抛出异常
*/
public String getContent() throws Exception{
BufferedReader reader = null;
StringBuilder builder = new StringBuilder();
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String temp = null;
while ((temp = reader.readLine()) != null) {
builder.append(temp + "\n");
}
content = builder.toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
throw e;
} finally {
if (reader != null) {
reader.close();
}
}
return content;
}
}
3.3 wordcount类的设计
wordcount类代码如下:
import java.util.List;
/**
* 获取字符串的各种信息
*/
public class WordCount {
private String content;
/**
* 所有行
*/
private int allLine = -1;
/**
* 空格
*/
private int space = -1;
/**
* 空行
*/
private int spaceLine = -1;
/**
* 代码行
*/
private int codeLine = -1;
/**
* 注释行
*/
private int NoteLine = -1;
private static final String ILLEGAL_CHARACTERS_REX = "[^a-zA-Z]";
private static final String BLANK_REX = " +";
private static final String NOTE_REX = "((//.*\\n)|(/\\*[.\\n]*\\*/))";
public WordCount(String content) {
this.content = content;
}
/**
* 获取字符串字符的数量
* @return 数量
*/
public int getContentLength() {
if (content != null) {
return content.length();
}else {
return 0;
}
}
/**
* 获取内容的行数
* @return 行数
*/
public int getContentLine() {
if (allLine == -1) {
if (content != null) {
String[] lines = content.split("\n");
if (lines.length == 1 && lines[0].equals("")) {
allLine = 0;
return allLine;
}
allLine = lines.length;
return allLine;
} else {
allLine = 0;
return allLine;
}
}else {
return allLine;
}
}
/**
* 获取空行
* @return 空行数量
*/
public int getSpaceLine() {
if (spaceLine == -1) {
if (content != null) {
int num = 0;
String[] fileLinse = content.split("\n");
for (int i = 0; i < fileLinse.length; i++) {
if (fileLinse[i].equals("")) {
num++;
}
}
spaceLine = num;
return spaceLine;
}else {
spaceLine = 0;
return spaceLine;
}
}else {
return spaceLine;
}
}
/**
* 获取注释行
* @return 注释行行数
*/
public int getNoteLine() {
List notes = new RexUtil(NOTE_REX,content).getRexString();
int lines = 0;
for (int i = 0; i < notes.size(); i++) {
String note = notes.get(i);
System.out.println(note);
if (note != null) {
String[] tip = note.split("\n");
lines += tip.length;
}
}
return lines;
}
/**
* 获取单词数目
* @return 单词数目
*/
public int getWordCount() {
//用正则表达式去除非法字符
if (content != null) {
String temp = content.replaceAll(ILLEGAL_CHARACTERS_REX, " ");
String[] words = temp.split(BLANK_REX);
if (words.length == 1 && words[0].equals("")) {
return 0;
}
return words.length - 1;
}else {
return 0;
}
}
}
4.运行测试
将代码通过命令行编译后:
创建测试文件test.c
此时文件为空文件
在命令行输入分别输入
java Main -c test.c
java Main -w test.c
java Main -l test.c
结果如下:
向test.c中输入内容:
测试结果:
递归处理测试:
创建test文件夹:
empty.c内容为空,其他文件都有一定的内容
测试结果:
5.项目总结
因为时间较赶,部分功能还未完成(相应的功能模块已经编写好了,但存在这一些bug还没来得及修复,因此并没有完成此功能)。
抽象能力有待提高,代码维护性有待提高。
期待下一次的项目。