1、GitHub地址:https://github.com/JQJQ2019/WC.gitjava
2、PSP表格:git
3、主要困難與解決:github
一、字符數、詞數、行數的統計,上一次寫相似的統計程序是在大二的Java課程做業上,因此過了一年的時間,對這個功能已經有些生疏。所以我去複習了一下基本的統計實現方法,也有複習一下課本。此次的要求和上次最大的不一樣其實就在於Java課程要求的只是純文本(.txt)的統計,此次功能要求提升了,要知足各類文件的統計例如:.c、.cpp、.java等。不過其實大體的思路仍是差很少的,主要就是獲取文件的內容,而後普通文本和程序源代碼或者其餘類型的文件可能編碼類型不一樣,我採用GBK編碼進行統計。字符數、行數這兩個功能仍是沒有太多困難的地方。詞數則須要重溫一下正則表達式的一些知識點,而後經過合適的分詞模式就能實現了。正則表達式
二、空白行數、代碼行數、註釋行數的統計,這個功能被歸爲拓展功能,不過其實我以爲這個功能的實現要比基本功能要簡單一下,我採用先統計註釋行,而後是代碼行,最後是空白行的思路。統計註釋行最重要的一點是,經常使用的兩種註釋(單行註釋、多行註釋)都應該有相應的判斷來進行統計。特別是多行註釋,咱們應該關注註釋的開頭(/*)和註釋結尾(*/),固然,中間的註釋行應該給予它們一個註釋狀態,利用判斷語句判斷該行是否爲註釋狀態,是則要繼續增長註釋行的數目。代碼行則經過判斷該行內容是否大於1,由於根據本項目的規則,只要不是空格或者控制符或者只有一個字符就屬於代碼行。如此最後的空白行無需複雜判斷就能統計。數組
三、-s功能,這個拓展功能比較特殊,我以爲也是整個項目有些困難的一部分。個人方法是經過String類的一些方法如substring()、lastIndexOf()等將用戶的輸入內容進行分割,獲取文件的路徑以及文件的類型。而後調用File類的listFiles()方法將全部文件存入File數組(File[ ]),而後foreach整個數組,並對每一個文件進行判斷,若符合相應格式的文件則加入到ArrayList中,便於後續的統計。app
四、圖形化界面的實現(-x功能),其實我認爲若是上面的功能都實現了的話,GUI是很快就能完成了。須要的組件比較少,主要就是JTextArea(用來顯示文件的統計結果)還有JButton(用來觸發事件)。主要就這兩個組件。而後建立好JFrame對象後,一加上去就能基本的界面了。稍微複雜一點的是給button加個監聽器,而後對接口方法的實現(ActionPerformed)。由於上面統計字符數、詞數、行數以及特殊行數的功能已經寫好了,那麼接口方法的實現就是調用一下各個統計方法,而後用先前定義好的變量接收返回值。最後調用JTextArea的append()方法將統計結果追加就好。框架
4、調用流程圖:函數
5、各種功能測試與詳細代碼:學習
一、主界面:
測試
二、錯誤命令演示:
三、處理不存在文件的演示:
四、字符數統計演示:
五、詞數的統計演示:
六、行數的統計演示:
七、也能夠多個命令一塊兒用:
八、特殊行數目的統計演示:
九、-s功能的演示:
全部符合要求的文件都已被統計
十、圖形化界面(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);
}
}
6、我的心得:
此次的我的項目我以爲仍是涉及到不少Java SE的知識點的,好比Swing、事件監聽、ArrayList以及其餘的一些小知識點。我以爲是讓咱們從新審視本身基礎的很是好的機會。在這個過程當中,我也碰到了很多問題。第1、我以爲本身沒有徹底考慮好總體的設計思路,因此就寫了又改改了有寫,因此我認爲不管是從此的學習或者是未來的工做,咱們都應該有一個比較好的思想框架而後再動手,畢竟磨刀不誤砍柴工。第2、我本身以爲不少其實應該掌握的經常使用方法沒有記牢好比String類的lastIndexOf寫成lastIndex找了好一下子才發現錯在這,所以仍是得多多實踐。第3、我本人對代碼並不滿意,代碼的複用性還有很多地方的邏輯還不夠清晰、簡明。就是說有些處理有點繞了。第4、GUI方面由於Swing用得並很少,因此整個圖形化界面也沒有多美觀,各種知識點都應該學得更紮實點。
不過項目最大的做用不就是明確本身的不足不是嗎,因此儘管有不少地方還須要改進,我也以爲此次項目給了我很大的幫助。