Java實現WordCount


GitHub項目地址https://github.com/happyOwen/SoftwareEngineering

wordcount項目要求:

程序處理用戶需求的模式為:wc.exe [parameter] [file_name]

  • 基本功能列表:

    • -c file.c   //返回文件 file.c 的字符數(實現)

    • -w file.c   //返回文件 file.c 的詞的數目 (實現)

    • -l file.c   //返回文件 file.c 的行數(實現)

  • 擴展功能:

    • -s 遞歸處理目錄下符合條件的文件(實現)

    • -a 返回更復雜的數據(代碼行 / 空行 / 注釋行)(實現)

      空行:本行全部是空格或格式控制字符,如果包括代碼,則只有不超過一個可顯示的字符,例如“{”。

      代碼行:本行包括多於一個字符的代碼。

      注釋行:本行不是代碼行,並且本行包括注釋。

    • [file_name]: 文件或目錄名,可以處理一般通配符(實現)

  • 高級功能:

    • -x 參數。這個參數單獨使用。如果命令行有這個參數,則程序會顯示圖形界面,用戶可以通過界面選取單個文件,

  程序就會顯示文件的字符數、行數等全部統計信息。(未實現)

PSP表:

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Planning 計划 30 60
· Estimate · 估計這個任務需要多少時間 30 60
Development 開發 1080 1680
· Analysis · 需求分析 (包括學習新技術) 100 200
· Design Spec · 生成設計文檔 50 100
· Design Review · 設計復審 (和同事審核設計文檔) 40 100
· Coding Standard · 代碼規范 (為目前的開發制定合適的規范) 10 20
· Design · 具體設計 50 150
· Coding · 具體編碼 600 800
· Code Review · 代碼復審 30 50
· Test · 測試(自我測試,修改代碼,提交修改) 200 250
Reporting 報告 120 150
· Test Report · 測試報告 90 120
· Size Measurement · 計算工作量 5 10
· Postmortem & Process Improvement Plan · 事后總結, 並提出過程改進計划 15 20
合計 1230 1990

  

解題思路:

剛看到這個題目的時候覺得有些無從下手,主要是對於擴展功能中的代碼行、空行、注釋行的含義理解不是很懂,以及不太明白如何
遞歸處理目錄下符合條件(以通配符表示)的文件,經過自己的思考和同學的討論,我發現主要是對文件的IO和對String的解析和正則表達式的匹配問題,所以我參考了網上有關正則表達式的教程並閱讀了java相關教材。

設計實現過程

以main()為中心,通過cmd傳入參數到main(String[] args)獲取操作和文件路徑,根據是否查詢子目錄調用getRecursiveFiles()和getFiles()獲取需要符合條件的文件,並將其絕對路徑添加到ArrayList中,最后遍歷ArrayList並根據相應的操作對相應的文件執行相應的操作方法。

總共設計了兩個類Main.java、CountUtils.java。Main.java類主要處理傳入的參數並根據參數執行CountUtils.java里面的功能函數

代碼說明

Main.java,通過args獲取要執行的操作,並根據是否遞歸來獲取所有符合條件的文件路徑,遍歷並執行相應的功能函數

package com.homework.first;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

public class Main {

	private static boolean IS_COUNTCHAR = false;
	private static boolean IS_COUNTWORD = false;
	private static boolean IS_COUNTLINE = false;
	private static boolean IS_RECURSION = false;
	private static boolean IS_COUNTDIFFLINE = false;
	//利用ArrayList存儲符合條件的文件的絕對路徑
	private static ArrayList<String> fileList = new ArrayList<String>();
	
	public static void main(String[] args) throws IOException,ArrayIndexOutOfBoundsException {
		//默認最后一個參數為路徑名
		String path = args[args.length - 1];

		CountUtils cUtils = new CountUtils();
		//判斷需要執行的功能
		for(int i = 0; i < args.length - 1; i++) {
			if(args[i].equals("-c")) {
				IS_COUNTCHAR = true;
			}
			if(args[i].equals("-w")) {
				IS_COUNTWORD = true;
			}
			if(args[i].equals("-l")) {
				IS_COUNTLINE = true;
			}
			if(args[i].equals("-s")) {
				IS_RECURSION = true;
			}
			if(args[i].equals("-a")) {
				IS_COUNTDIFFLINE = true;
			}
		}
		//獲取目錄名
		String paths[] = path.split("\\\\");
		StringBuilder sb = new StringBuilder();		
		for(int i=0;i<paths.length-1;i++) {
			if(i==paths.length-2) {
				sb.append(paths[i]);
			}else {
				sb.append(paths[i]+"\\");
			}
		}
		
		String dirName = sb.toString();
		File file = new File(dirName);
		if(!file.isDirectory()) {
			System.out.println("路徑錯誤!");
		}
		String fileName =paths[paths.length-1];
		//對文件名通配符處理
		fileName = fileName.replaceAll("\\*", "\\.+").replaceAll("\\?", "\\.");
		
		//若IS_RECURSION,則使用遞歸獲取文件名(包括子目錄),否則只獲取當前目錄下符合條件的文件名
		if(IS_RECURSION) {
			cUtils.getRecursionFiles(dirName, fileName);
		}else {
			cUtils.getFiles(dirName, fileName);
		}
		fileList = cUtils.fileList;
		
		//遍歷fileList,對每一個文件使用選擇的功能
		for(String item : fileList) {
			System.out.println("文件路徑為:"+item);
			if(IS_COUNTCHAR) {
				cUtils.countChar(item);
			}
			if(IS_COUNTWORD) {
				cUtils.countWord(item);
			}
			if(IS_COUNTLINE) {
				cUtils.countLine(item);
			}
			if(IS_COUNTDIFFLINE) {
				cUtils.countDiffLine(item);
			}
		}
	}
	

}

以下是相應的功能函數:

  • -c
//利用BufferedReader整行讀取統計字符數
	public int countChar(String path) {
		File file = new File(path);
		BufferedReader br = null;
		String line;
		int charNum = 0;
		try {
			br = new BufferedReader(new FileReader(file));
			while((line = br.readLine()) != null){
				char[] ch = line.toCharArray();
				for(int i=0; i < ch.length; i++) {
					if(!Character.isWhitespace(ch[i])) {
						charNum++;
					}
				}
			}
			System.out.println("the charCount is: " + charNum);
			br.close();
		} catch (Exception e) {
			System.out.println(path + "文件名錯誤");
		}	
		return charNum;
	}
  • -w
/*
	 * 統計英文單詞數
	 * 先用BufferedReader整行讀取,然后添加到StringBuffer中,
	 * 將StringBuffer轉化為字符串后,然后將非英文字符替換為空格,再根據空格分割
	 */
	public int countWord(String path) {
		BufferedReader br = null;
		String line;
		String[] strings;
		StringBuffer sbf = new StringBuffer();
		int wordNum = 0;
		String reg = "\\s+";
		try {
			br = new BufferedReader(new FileReader(path));
			while((line = br.readLine()) != null){
				sbf.append(line);								
			}
			String s = sbf.toString().replaceAll("[^a-zA-Z]", " ");			
			strings = s.split(reg);
			wordNum = strings.length;
			System.out.println("the wordCount is: " + wordNum);
			br.close();
		} catch (IOException e) {
			System.out.println(path + "文件名錯誤");
		}			
		return wordNum;
	}
  • -l
//統計總行數
	public int countLine(String path) {
		BufferedReader br = null;
		String line;
		int lineNum = 0;
		try {
			br = new BufferedReader(new FileReader(path));
			while((line = br.readLine()) != null){
				lineNum++;				
			}
			System.out.println("the lineCount is: " + lineNum);
			br.close();
		} catch (IOException e) {
			System.out.println(path + "文件名錯誤");
		}			
		return lineNum;
	}
  • -a
//統計注釋行、空行、代碼行
	public void countDiffLine(String path) {
		int annotationLineNum = 0;
		int codeLineNum = 0;
		int nullLineNum = 0;
		String line;
		BufferedReader br = null;
		// 注釋匹配器(匹配單行、多行、文檔注釋)
        Pattern annotationLinePattern = Pattern.compile("(//)|(/\\*)|(^\\s*\\*)|(^\\s*\\*+/)");    
		try {
			br = new BufferedReader(new FileReader(path));
			while((line = br.readLine()) != null){
				if(annotationLinePattern.matcher(line).find()) {//注釋行		
					annotationLineNum++;
				}else if (line.matches("\\s*\\p{Graph}?\\s*")) {//空行
					nullLineNum++;
				}else {					
					codeLineNum++;
				}				
			}
			System.out.println("the nullLineNum is: " + nullLineNum);
			System.out.println("the annotationLineNum is: " + annotationLineNum);
			System.out.println("the codeLineNum is: " + codeLineNum);
			br.close();
		} catch (IOException e) {
			System.out.println(path + "文件名錯誤");
		}	
	}
  • 遞歸獲取符合條件的文件的絕對路徑(在Main.java中已對fileName進行通配符處理):
public void getRecursionFiles(String dirName,String fileName) {
		File file = new File(dirName);
		if(file.isDirectory()) {
			File[] files = file.listFiles();
			if(files!=null) {
				for(File file1 : files) {
				    //當file1仍然是目錄時,遞歸調用此函數
					if(file1.isDirectory()) {					
						getRecursionFiles(dirName+"\\"+file1.getName(),fileName);
					}else {
					    //處理后的fileName作為匹配規則,對遍歷的文件進行匹配
						Pattern pattern = Pattern.compile(fileName);
						Matcher m = pattern.matcher(file1.getName());
						if(m.matches()) {
							fileList.add(dirName+"\\"+file1.getName());
						}
					}
				}
			}
		}
	}
  • 非遞歸獲取符合條件的文件的絕對路徑(在Main.java中已對fileName進行通配符處理):
//僅獲取當前目錄下符合條件的文件,並將其絕對路徑添加到ArrayList
	public void getFiles(String dirName,String fileName) {
		File file = new File(dirName);
		if(file.isDirectory()) {
			File[] files = file.listFiles();
			if(files != null) {
				for(File file1 : files) {
					if(!file1.isDirectory()) {
						Pattern pattern = Pattern.compile(fileName);
						Matcher m = pattern.matcher(file1.getName());
						if(m.matches()) {
							fileList.add(dirName+"\\"+file1.getName());
						}
					}
				}
			}
			
		}
	}

測試運行



項目小結

這次軟件工程作業不僅讓我了解到github、exe4j、單元測試等工具的使用,而且我也重新回顧了java和正則表達式的相關知識,在這個過程中,我初步體驗到了一個項目開發的完整流程。在項目開發的初始,我急於實現各個功能的代碼,導致后來各個函數調用之間存在難以對接以致多次修改的情況,之后我及時調整,按照軟件開發的流程,逐步實現,最后得以完成。總的來說,我的編程能力和思維能力還有待提高,我會有計划的去學習java等知識。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM