WC程序(Java实现)

一、GitHub地址:https://github.com/JQJQ2019/WC.git

二、PSP表格:

三、主要困难与解决:

1、字符数、词数、行数的统计,上一次写类似的统计程序是在大二的Java课程作业上,所以过了一年的时间,对这个功能已经有些生疏。因此我去复习了一下基本的统计实现方法,也有复习一下课本。这次的要求和上次最大的不同其实就在于Java课程要求的只是纯文本(.txt)的统计,这次功能要求提高了,要满足各种文件的统计例如:.c、.cpp、.java等。不过其实大致的思路还是差不多的,主要就是获取文件的内容,然后普通文本和程序源代码或者其他类型的文件可能编码类型不同,我采用GBK编码进行统计。字符数、行数这两个功能还是没有太多困难的地方。词数则需要重温一下正则表达式的一些知识点,然后通过合适的分词模式就能实现了。

2、空白行数、代码行数、注释行数的统计,这个功能被归为拓展功能,不过其实我觉得这个功能的实现要比基本功能要简单一下,我采用先统计注释行,然后是代码行,最后是空白行的思路。统计注释行最重要的一点是,常用的两种注释(单行注释、多行注释)都应该有相应的判断来进行统计。特别是多行注释,我们应该关注注释的开头(/*)和注释结尾(*/),当然,中间的注释行应该给予它们一个注释状态,利用判断语句判断该行是否为注释状态,是则要继续增加注释行的数目。代码行则通过判断该行内容是否大于1,因为根据本项目的规则,只要不是空格或者控制符或者只有一个字符就属于代码行。如此最后的空白行无需复杂判断就能统计。

3、-s功能,这个拓展功能比较特殊,我觉得也是整个项目有些困难的一部分。我的方法是通过String类的一些方法如substring()、lastIndexOf()等将用户的输入内容进行分割,获取文件的路径以及文件的类型。然后调用File类的listFiles()方法将所有文件存入File数组(File[ ]),然后foreach整个数组,并对每个文件进行判断,若符合相应格式的文件则加入到ArrayList中,便于后续的统计。

4、图形化界面的实现(-x功能),其实我认为如果上面的功能都实现了的话,GUI是很快就能完成了。需要的组件比较少,主要就是JTextArea(用来显示文件的统计结果)还有JButton(用来触发事件)。主要就这两个组件。然后创建好JFrame对象后,一加上去就能基本的界面了。稍微复杂一点的是给button加个监听器,然后对接口方法的实现(ActionPerformed)。因为上面统计字符数、词数、行数以及特殊行数的功能已经写好了,那么接口方法的实现就是调用一下各个统计方法,然后用先前定义好的变量接收返回值。最后调用JTextArea的append()方法将统计结果追加就好。

四、调用流程图

五、各类功能测试与详细代码:

1、主界面:

2、错误命令演示:

3、处理不存在文件的演示:

4、字符数统计演示:

5、词数的统计演示:

6、行数的统计演示: 

7、也可以多个命令一起用:

8、特殊行数目的统计演示:

9、-s功能的演示:

所有符合要求的文件都已被统计

10、图形化界面(GUI)演示:

详细代码如下:

注释应该比较齐全了,除了太不需要解释的地方都带有注释

import java.util.Scanner;
import java.io.*;
import java.util.regex.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class HomeworkTestDrive{
//main函数用来展示一下该程序的使用说明
public static void main(String[] args){
//声明一个文件处理器的变量
FileProcessor fileProcessor;
while(true){
System.out.println("-------------------WC.EXE-------------------");
System.out.println("基本功能使用说明:");
System.out.println("统计字符数:-c [fileName]");
System.out.println("统计词数:-w [fileName]");
System.out.println("统计行数:-l [fileName]");
System.out.println("");
System.out.println("拓展功能使用说明:");
System.out.println("统计空行、代码行、注释行:-a [fileName]");
System.out.println("递归处理目录下符合条件的文件:-s [fileName]");
System.out.println("");
System.out.println("高级功能使用说明:");
System.out.println("图形化界面:-x");
System.out.println("--------------------------------------------");

System.out.println("请输入指令:");
Scanner command = new Scanner(System.in);
//将用户输入的命令通过分离空格分成用户指令和文件分开存进数组中
String[] arr = command.nextLine().split("\\s");
int len = arr.length;
//创建文件处理器对象,通过该对象执行用户指令
fileProcessor = new FileProcessor();
fileProcessor.operateByCommand(arr,len,0,arr[arr.length-1]);
}
}
}

class FileProcessor{
public void operateByCommand(String[] arr,int len,int start,String fileName){
if(arr[0].equals("-x")){
WcGui wcGui = new WcGui();
wcGui.go();
}else{
try{
for(int i = start;i < len-1;i++){
//使用GBK编码
String encoding = "GBK";
//根据文件名创建File对象
File file = new File(fileName);
//传入FileInputStream和指定编码创建InputStream对象
InputStreamReader readFile = new InputStreamReader(new FileInputStream(file),encoding);
//利用BufferedReader对象可以比较方便地统计字符数、行数等等
BufferedReader fileContent = new BufferedReader(readFile);
//根据不同的用户指令选择要调用的方法
switch(arr[i]){
case "-c":
countChars(fileContent);
break;
case "-w":
countWords(fileContent);
break;
case "-l":
countLines(fileContent);
break;
case "-a":
countSpecial(fileContent);
break;
case "-s":
recursiveProcessing(arr);
break;
default:
System.out.println("哥哥...输入上面说明的正确指令啊......");
}
}
}catch(Exception e){
System.out.println("亲,这边找不到文件呢~");
e.printStackTrace();
}
}
}

public static int countChars(BufferedReader fileContent) throws IOException{
//记录文件每一行的内容
String lineContent = null;
//定义一个变量记录字符数
int charNum = 0;
while((lineContent = fileContent.readLine()) != null){
//调用trim()方法,去掉字符串两端的空格,方便统计
lineContent = lineContent.trim();
for(int i = 0;i < lineContent.length();i++){
//利用循环依此获取每个字符
char ch = lineContent.charAt(i);
//如果不是空格、换行符、制表符就将charNum加1
if(ch != '\n' && ch != '\t' && ch != ' ')
charNum++;
}
}
System.out.println("字符数:" + charNum);
return charNum;
}

public static int countWords(BufferedReader fileContent) throws IOException{
//定义一个正则表达式
String REGEX = "[a-zA-Z]+\\b";
//定义一个匹配模式
Pattern pattern = Pattern.compile(REGEX);
//记录文件每一行的内容
String lineContent = null;
//定义一个变量记录词数
int wordNum = 0;
while((lineContent = fileContent.readLine()) != null){
lineContent = lineContent.trim();
Matcher matcher = pattern.matcher(lineContent);
while(matcher.find()){
wordNum++;
}
}
System.out.println("词数:" + wordNum);
return wordNum;
}

public static int countLines(BufferedReader fileContent) throws IOException{
//记录文件每一行的内容
String lineContent = null;
//定义一个变量记录行数
int lineNum = 0;
while((lineContent = fileContent.readLine()) != null){
//只要这行不为空,则lineNum加1
lineNum++;
}
System.out.println("行数:" + lineNum);
return lineNum;
}

public static ArrayList<Integer> countSpecial(BufferedReader fileContent)throws IOException{
ArrayList<Integer> resultList = new ArrayList<Integer>();

String lineContent = null; //用来保存每行的内容
boolean isComment = false; //记录当前行是否属于注释状态

int codeLineNum = 0; //记录代码行数
int blankLineNum = 0; //记录空行数
int annotationLineNum = 0; //记录注释行数

while((lineContent = fileContent.readLine()) != null){
//多行注释的开始
if(lineContent.contains("/*")){
annotationLineNum++;
isComment = true;
}else if(isComment){
annotationLineNum++;
//多行注释的结束
if(lineContent.contains("*/")){
isComment = false;
}
}else if(lineContent.contains("//")){
//单行注释,注释行数加1
annotationLineNum++;
}else if(lineContent.trim().length() > 1){
codeLineNum++;
}else{
blankLineNum++;
}
}

System.out.println("空行数:" + blankLineNum);
System.out.println("代码行数:" + codeLineNum);
System.out.println("注释行数:" + annotationLineNum);
resultList.add(blankLineNum);
resultList.add(codeLineNum);
resultList.add(annotationLineNum);
return resultList;
}

public static void recursiveProcessing(String[] arr) throws IOException{
String fileDir = arr[arr.length-1].substring(0,arr[arr.length-1].lastIndexOf("\\"));
String fileFilter = arr[arr.length-1].substring(arr[arr.length-1].lastIndexOf("."));

List<File> fileList = new ArrayList<File>();
File file = new File(fileDir);// 指定查找目录
File[] files = file.listFiles();// 获取目录下的所有文件或文件夹
if (files == null) {// 如果目录为空,直接退出
return;
}
// 遍历files中的所有文件
for (File f : files) {
if (f.isFile()&&f.getName().endsWith(fileFilter)) {
fileList.add(f);
//System.out.println(f.getName());
}
}
for (File f1 : fileList) {
System.out.println(f1.getName());
for(int i = 0;i < 4;i++){
/*原来发现不加这个循环,四个方法只有统计字符有结果,我觉得是BufferedReader的readLine()方法
一直读取下一行的原因,所以增加了一个内循环,每次都重新生成新的BufferedReader,四个方法都能得到结果。*/
String encoding = "GBK";
InputStreamReader readFile = new InputStreamReader(new FileInputStream(f1),encoding);
BufferedReader fileContent = new BufferedReader(readFile);
switch(i){
case 0:
countChars(fileContent);
break;
case 1:
countWords(fileContent);
break;
case 2:
countLines(fileContent);
break;
case 3:
countSpecial(fileContent);
break;
}
}
//画个分界线看起来清楚些
System.out.println("-----------------------------");
}
}
}

class WcGui implements ActionListener{
JFrame frame;
JTextArea textArea;
int charNum = 0; //用来接收上面统计字符数方法的返回值
int wordNum = 0; //用来接收上面统计词数方法的返回值
int lineNum = 0; //用来接收上面统计行数方法的返回值
ArrayList<Integer> resultList = null; //接收上面统计特殊行数目方法的返回值,调用get()方法给下面三个变量赋值
int blankLineNum = 0;
int codeLineNum = 0;
int annotationLineNum = 0;

//简单的Gui,创建JFrame对象、用来显示结果的TextArea、以及一个button用来监听
public void go(){
frame = new JFrame("文件统计GUI");
textArea = new JTextArea();
frame.getContentPane().add(BorderLayout.CENTER,textArea);

JButton button = new JButton("选择文件并进行各类统计");
frame.getContentPane().add(BorderLayout.SOUTH,button);
button.addActionListener(this);

frame.setSize(500,500);
frame.setVisible(true);
}

public void actionPerformed(ActionEvent event){
JFileChooser chooser = new JFileChooser(); //创建JFileChooser对象用来选择要统计的文件
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); //限定被选文件形式
chooser.showDialog(new JLabel(), "选择"); //选择框
File file = chooser.getSelectedFile(); //获取文件
for(int i = 0;i < 4;i++){ //调用上面写好的方法,并将返回值赋给相应变量
try{
String encoding = "GBK";
InputStreamReader readFile = new InputStreamReader(new FileInputStream(file),encoding);
BufferedReader fileContent = new BufferedReader(readFile);
switch(i){
case 0:
charNum = FileProcessor.countChars(fileContent);
break;
case 1:
wordNum = FileProcessor.countWords(fileContent);
break;
case 2:
lineNum = FileProcessor.countLines(fileContent);
break;
case 3:
resultList = FileProcessor.countSpecial(fileContent);
blankLineNum = resultList.get(0);
codeLineNum = resultList.get(1);
annotationLineNum = resultList.get(2);
break;
}
}catch(IOException e){
System.out.println("你的文件到火星去啦~");
e.printStackTrace();
}
}
//将结果展示在TextArea上
textArea.append("字符数:" + charNum + "\n词数:" + wordNum + "\n行数:" + lineNum + "\n空行数:" + blankLineNum + "\n代码行数:" + codeLineNum + "\n注释行数" + annotationLineNum);
}
}

六、个人心得:

  这次的个人项目我觉得还是涉及到很多Java SE的知识点的,比如Swing、事件监听、ArrayList以及其他的一些小知识点。我觉得是让我们重新审视自己基础的非常好的机会。在这个过程中,我也碰到了不少问题。第一、我觉得自己没有完全考虑好整体的设计思路,所以就写了又改改了有写,所以我认为无论是今后的学习或者是将来的工作,我们都应该有一个比较好的思想框架然后再动手,毕竟磨刀不误砍柴工。第二、我自己觉得很多其实应该掌握的常用方法没有记牢比如String类的lastIndexOf写成lastIndex找了好一会儿才发现错在这,因此还是得多多实践。第三、我本人对代码并不满意,代码的复用性还有不少地方的逻辑还不够清晰、简明。就是说有些处理有点绕了。第四、GUI方面因为Swing用得并不多,所以整个图形化界面也没有多美观,各类知识点都应该学得更扎实点。

  不过项目最大的作用不就是明确自己的不足不是吗,所以尽管有很多地方还需要改进,我也觉得这次项目给了我很大的帮助。

猜你喜欢

转载自www.cnblogs.com/wzqwzq/p/11569739.html