Github项目地址:https://github.com/Aliiiiiiiiiiiiiiiii/Handsome-boy
一.项目要求
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:wc.exe [parameter]
[file_name]
基本功能列表:
wc.exe -cfile.c //返回文件
file.c 的字符数
wc.exe -w
file.c //返回文件
file.c 的词的数目
wc.exe -lfile.c //返回文件
file.c 的行数
扩展功能:
-s 递归处理目录下符合条件的文件。
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释
在这种情况下,这一行属于注释行。
[file_name]: 文件或目录名,可以处理一般通配符。
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
二.PSP表格
PSP2.1
Personal Software Process Stages
预估耗时(分钟)
实际耗时(分钟)
Planning
计划
120
90
· Estimate
· 估计这个任务需要多少时间
30
60
Development
开发
1440
1440
· Analysis
· 需求分析 (包括学习新技术)
120
120
· Design Spec
· 生成设计文档
30
50
· Design Review
· 设计复审 (和同事审核设计文档)
20
30
· Coding Standard
· 代码规范 (为目前的开发制定合适的规范)
10
20
· Design
· 具体设计
60
90
· Coding
· 具体编码
50
100
· Code Review
· 代码复审
600
780
· Test
· 测试(自我测试,修改代码,提交修改)
300
250
Reporting
报告
150
150
· Test Report
· 测试报告
60
90
· Size Measurement
· 计算工作量
5
10
· Postmortem & Process Improvement Plan
· 事后总结, 并提出过程改进计划
20
20
合计
1305
1620
三.解题思路
刚拿到题目的时候我并没有想到使用正则表达式去做匹配功能,后来跟同学讨论才知道的,看到对文件的操作就自然想到了学过的Java的输入输出流,对文件的操作也是比较了解的,所以选择了使用java去实现wc功能,至于这么多功能,我选择分模块去写代码,一个类实现一个功能,然后写一个main函数利用switch对用户输入的操作指令进行各项功能的实现,或许会有一些代码重复,但是思路直接,实现起来思路会清晰。统计字符使用正则表达式很容易就实现了,统计单词利用Pattern的split()方法把[^a-zA-Z]的分隔开,利用String[]存储再统计,行的统计就直接读一行就LineCount++。至于图形化界面,之前有学过相关的知识,所以是实现起来比较快,主要是输出文件的内容以及-c,-w,-l的实现。
四.设计实现过程
通过用户的输入而进入不同的功能模块,每个功能为一个类,通过while循环包含着switch语句进行可重复的操作
五.代码说明
主函数代码
packagewc;importjava.io.IOException;importjava.util.Scanner;public classStart {public static void main(String[] args) throwsIOException {//TODO 自动生成的方法存根
System.out.println("Welcome to wc.exe!\n");
System.out.println("----------指令参考-----------");
System.out.println("wc.exe -c 返回文件的字符数");
System.out.println("wc.exe -w 返回文件的单词数");
System.out.println("wc.exe -l 返回文件的行数");
System.out.println("wc.exe -s 递归处理目录下符合条件的文件");
System.out.println("wc.exe -a 返回代码行 / 空行 / 注释行");
System.out.println("wc.exe -x 显示图形界面");
System.out.println("wc.exe -exit 退出");
System.out.println("-------------------------------------\n");while(true) {
System.out.print("请输入指令: wc.exe -");
Scanner s=newScanner(System.in);
String in=s.nextLine();switch(in) { //输入不同的指令打开不同的类,实现不同的功能
case "c":
CharCount cc=newCharCount();break;case "w":
WordCount wc=newWordCount();break;case "l":
LineCount lc=newLineCount();break;case "s":
SCount sc=newSCount();break;case "a":
ACount ac=newACount();break;case "x":
HighLevel hl=newHighLevel();break;case "exit":
System.out.println("Good Bye!");
System.exit(0);default:
System.out.println("请输入正确的命令");
}
}
}
}
CharChout 字符统计
packagewc;importjava.io.BufferedReader;importjava.io.FileReader;importjava.io.IOException;importjava.util.Scanner;importjava.util.regex.Matcher;importjava.util.regex.Pattern;public classCharCount {
String REGEX="\\S"; //非空白字符
CharCount()throwsIOException{
Pattern p=Pattern.compile(REGEX); //利用正则表达式Pattern类以及Matcher类
System.out.println("请输入文件的绝对路径:");
System.out.print("wc.exe -c ");
Scanner inc=newScanner(System.in);
String path=inc.nextLine();
BufferedReader fis=new BufferedReader(newFileReader(path));
String s;int charcount=0;while((s=fis.readLine())!=null) {
Matcher m=p.matcher(s); //对正则表达式进行模式匹配
while(m.find())
charcount++;
}
System.out.println("该文件有"+charcount+"个字符\n\n");
fis.close();
}
}
WordCount 单词统计
public classWordCount {
String REGEX="[^a-zA-Z]";
WordCount()throwsIOException{
Pattern p=Pattern.compile(REGEX);
System.out.println("请输入文件的绝对路径:");
System.out.print("wc.exe -w ");
Scanner inw=newScanner(System.in);
String path=inw.nextLine();
BufferedReader fis=new BufferedReader(newFileReader(path));
String s;int wordcount=0;while((s=fis.readLine())!=null) {if(s.isEmpty()==false) { //要不是一行空行才进行下面的操作,不然要是有空行的话结果不对
String[] letter=p.split(s);for(String word : letter)
wordcount++;
}
}
System.out.println("该文件有"+wordcount+"个单词\n\n");
fis.close();
}
}
LineCount 行数统计
public classLineCount {
LineCount()throwsIOException{
System.out.println("请输入文件的绝对路径");
System.out.print("wc.exe -l ");
Scanner inl=newScanner(System.in);
String path=inl.nextLine();
BufferedReader fis=new BufferedReader(newFileReader(path));
String s;int linecount=0;while((s=fis.readLine())!=null) { //直接读到一行就+1
linecount++;
}
System.out.println("该文件有"+linecount+"行\n\n");
fis.close();
}
}
Scount 递归处理目录下符合条件的文件
public classSCount {
SCount()throwsIOException{
System.out.print("请输入要执行的操作:wc.exe -s -");
Scanner control=newScanner(System.in);
String ct=control.nextLine();
System.out.print("请输入要递归处理的目录路径:");
Scanner in=newScanner(System.in);
String path=in.nextLine();
System.out.print("请输入要递归处理的文件类型:wc.exe -s -"+ct+" *.");
Scanner style=newScanner(System.in);
String st=style.nextLine();
File file=newFile(path);
String[] name=file.list();
String s;int count=0;switch(ct) {case "c":
Pattern cp=Pattern.compile("\\S");for(String n:name) {if(n.matches("\\S+."+st)) { //找出目录下符合文件后缀的文件
BufferedReader fis=new BufferedReader(new FileReader(path+"\\"+n));while((s=fis.readLine())!=null) { //这里的操作跟前面的模块一样
Matcher m=cp.matcher(s);while(m.find())
count++;
}
System.out.println(n+" 中有"+count+"个字符");
count=0;
fis.close();
}
}break;case "w":
Pattern wp=Pattern.compile("[^a-zA-Z]");for(String n:name) {if(n.matches("\\S+."+st)) {
BufferedReader fis=new BufferedReader(new FileReader(path+"\\"+n));while((s=fis.readLine())!=null) {if(s.isEmpty()==false) {
String[] letter=wp.split(s);for(String word : letter)
count++;
}
}
System.out.println(n+" 中有"+count+"个单词");
count=0;
fis.close();
}
}break;case "l":for(String n:name) {if(n.matches("\\S+."+st)) {
BufferedReader fis=new BufferedReader(new FileReader(path+"\\"+n));while((s=fis.readLine())!=null) {
count++;
}
System.out.println(n+" 中有"+count+"行");
count=0;
fis.close();
}
}break;case "a":
}
}
}
ACount 返回更复杂的数据(代码行/ 空行/ 注释行)
public classACount {int total=0;int EmptyLine=0;int DescribeLine=0;int CodeLine=0;int tag=0; //设置一个标志,要tag=0,证明这一行还没扫描到“/*”,tag=1说明已经扫到了
ACount() throwsIOException{
Pattern p1=Pattern.compile("//");
Pattern p2=Pattern.compile("//*");
Pattern p3=Pattern.compile("/*/");
System.out.println("请输入文件的绝对路径:");
System.out.print("wc.exe -a ");
Scanner ina=newScanner(System.in);
String path=ina.nextLine();
BufferedReader fis=new BufferedReader(newFileReader(path));
String s;while((s=fis.readLine())!=null) {
total++;if(tag==0) { //根据tag的值进行不同的操作
Matcher m1=p1.matcher(s);
Matcher m2=p2.matcher(s);if(s.isEmpty()||(s.startsWith("{")&&s.endsWith("{"))) //只有tag=0时才计算空行,要是在多行注释里面出现的空行算入注释行而不算空行
EmptyLine++;else if(m1.find())
DescribeLine++;else if(m2.find()) {
DescribeLine++;
tag=1;
}
}else{
DescribeLine++;
Matcher m3=p3.matcher(s);if(m3.find()) //当匹配到“*/”的时候,说明多行注释结束,把tag置为0
tag=0;
}
}
CodeLine=total-DescribeLine-EmptyLine;
System.out.println("该文件有"+EmptyLine+"行空行");
System.out.println("该文件有"+DescribeLine+"行注释行");
System.out.println("该文件有"+CodeLine+"行代码行\n\n");
fis.close();
}
}
- x 显示图形界面的代码不在这里分析了,详细见Github源码
六.测试运行
七.项目小结
对于SCount以及ACount这两个类的语言逻辑,我出bug很多次了,总会存在一些小问题,后来发现自己的逻辑不够严谨,导致了运行结果的错误,而且整个程序还存在着或多或少的问题,有一些解决方法是我在上下学路上时候想到的,或许不是什么好的方法,但是这是我自己思考得出的方法,觉得通过这个项目的编程,我收获了不少,尽管最后做出来的程序有不足,但是通过自己思考和努力,锻炼了自己的编程能力,受益匪浅。