Java 實現的 簡單WordCount功能


githup

鏈接:https://gitee.com/iy2524/WordCount.git

PSP表格

 psp2.1  psp階段

 估計耗時(分鍾)

 實際耗時(分鍾)
Planning  計划  40  20
 · Estimate  · 估計這個任務需要多少時間  40 20
Development  開發 490  450
 · Analysis  · 需求分析 (包括學習新技術)  30  30
 · Design Spec  · 生成設計文檔  60  50
 · Design Review  · 設計復審 (和同事審核設計文檔)  60  90
 · Coding Standard  · 代碼規范 (為目前的開發制定合適的規范)  10  5
 · Design  · 具體設計  50  55
 · Coding  · 具體編碼  130  100
 · Code Review  · 代碼復審  60  60
 · Test  · 測試(自我測試,修改代碼,提交修改)  90  60
Reporting  報告  60  70
 · Test Report  · 測試報告  40  40
 · Size Measurement  · 計算工作量  10  5

 · Postmortem & ProcessImprovement Plan

 · 事后總結, 並提出過程改進計划  10  25
   合計  590 540

 

 以前沒有使用過psp表格,本次也是項目完成后才補上的,順序倒置了。在項目開發中,一天中斷斷續續地去做項目,具體時間也不好估計,沒有想過關注這些,所以也是大概寫了下數據。對於本項目自己的估計也不是很准確,更階段應該做什么事情用多少時間也沒經驗。不過經過此次項目,以后會學着使用,psp相當於想做計划然后與實際比照,計划時候有助於理清思路,比照時候有助於了解實際開發情況進度,對能力也有一定把握。

解題思路

WordCount功能為統計文件中信息,主要涉及的知識為文件IO流和對字符串的處理。我選擇熟悉的java來完成此項目。

一開始看程序預期運行效果和過程,對命令模式啟動程序一無所知,百度后發現args中即存儲的命令行。

程序測試的時候問題又來了,所需要的args參數該如何加入?百度后又得知在運行配置和調試配置中可以設置,在此,解決了絕大多數問題。

 

程序實現及代碼說明

WordCount功能為統計文件中信息,則按照功能需求依次先完成各種數據的統計,以下是具體功能實現代碼

 

所有對文件的統計方法寫在此工具類中

涉及到的知識:對文件IO流的操作,對字符串的操作

以下是此類總覽,詳細見后面,具體完整代碼點最上面碼雲鏈接

public class WordCount {
    private String input;
    
    private String output;
    
    private String stop;
    
    public static String result="resource/result.txt";
    
    public WordCount(String input,String stop,String output) throws FileNotFoundException
    {
        this.input=input;
        if(output==null)//未指定輸出文件 則默認為result
        {
            this.output=result;
        }
        else
        {
            this.output=output;
        }
        this.stop=stop;
    }
    
    //返回文件中的字符數
    public void  CountChars() throws IOException
    {
        ...
    }
    
    //返回文件中的總行數
    public void CountLines() throws IOException
    {
        ...
    }
    
    //返回文件中的總單詞數
    public void CountWords() throws IOException
    {
        ...
    }
    
    //返回 代碼行/空行/注釋行 的行數
    public void CountLinesByKind()throws IOException
    {
        ...
    }
    
    //使用停用詞文件 統計單詞個數
    public void CountWordsWithLimite() throws IOException
    {
        ...
    }
}

java對文件操作的方法有很多,此處主要用了BufferedInputStream和BufferedOutputStream,利用緩沖可以加快效率和速度

所有功能的操作都差不多:(1)讀取文件信息(2)處理文件信息(3)將結果寫入文件

下面是統計字符個數的完整代碼

    public void  CountChars() throws IOException
    {
        BufferedInputStream input= new BufferedInputStream(
                new FileInputStream(this.input));
        BufferedOutputStream output=new BufferedOutputStream(
                new FileOutputStream(this.output,true));
        int count=0;
        int x=-1;
        char ch;
        while((x=input.read())!=-1) //流讀取完的標志是返回-1  其他時候返回的為字符的ascii值
        {
            ch=(char)x;
            if(ch!='\r'&&ch!='\n')//換行\n和回車\r不統計為字符
            {
                count++;
            }
        }
        input.close();
        String str=this.input+",字符數:"+count;
        output.write(str.getBytes());
        output.write("\r\n".getBytes()); //回車 /r和換行/n位置不可換
        output.flush();
        output.close();
    }

下面是其他方法的關鍵代碼

    //返回文件中的總行數
    public void CountLines() throws IOException
    {
... int count=0; int x=-1; while((x=input.read())!=-1) { if((char)x=='\n')//遇到換行符表示一行結束 { count++; } } count++; ... }
    //返回文件中的總單詞數
    public void CountWords() throws IOException
    {
        ...
        int count=0;
        int x=-1;
        boolean InWord=false;//是否讀取到某個單詞中
        char ch='0';
        while(true)
        {   
            x=input.read();
            ch=(char)x;
            if(Character.isLetter(ch)&&InWord==false)//第一次讀取到字母 則讀取進入到單詞中
            {
                InWord=true;
            }
            else if(!Character.isLetter(ch)&&InWord==true)//讀取到單詞中時 讀取到第一個非字母的字符 則讀完一個單詞
            {
                count++;
                InWord=false;
            }
            if(x==-1)
            {
                break;
            }
        }
         ...
    }
    //返回 代碼行/空行/注釋行 的行數
    public void CountLinesByKind()throws IOException
    {
        ...
        int all_lines=0;
        int blank_lines=0;
        int note_lines=0;
        int code_lines=0;
        String strLine=null;
        boolean InNoteLines=false;
        
        //bug:若文件末尾有多個回車 則尾不可讀  導致總行數少1 未解決
        while((strLine=input.readLine())!=null)
        {
            all_lines++;
            strLine.replaceAll("\r", "");//去除換行符和空格 便於后面操作
            strLine.replaceAll("\n", "");
            strLine=strLine.trim();
            strLine.replaceAll(" ", "");
            if(InNoteLines==true)
            {
                note_lines++;
                if(strLine.endsWith("*/")||strLine.endsWith("*/}"))
                {
                    InNoteLines=false;
                }
            }
            else if(strLine.startsWith("/*")||strLine.startsWith("{/*")) //進入注釋行
            {
                note_lines++;
                if(!strLine.endsWith("*/")&&!strLine.endsWith("*/}"))//本行未注釋結束
                {
                    InNoteLines=true;
                }
            }
            else if(strLine.startsWith("//")||strLine.startsWith("{//"))
            {
                note_lines++;
            }
            else if(strLine.equals("")||strLine.equals("{")||strLine.equals("}"))
            {
                blank_lines++;
            }
        }
        code_lines=all_lines-blank_lines-note_lines;
...
}
//使用停用詞文件 統計單詞個數
    public void CountWordsWithLimite() throws IOException
    {
... int count=0; int x=-1; char ch; String stopStr=""; String str=""; List<String> stopList =new ArrayList<>(); boolean InWord=false; //取出停用詞裝到stopList while(true) { x=stop.read(); if(x==-1) { stopList.add(stopStr); stopStr=""; stop.close(); break; } ch=(char)x; if(ch==' ') { stopList.add(stopStr); stopStr=""; } else { stopStr+=ch; } } while(true) { x=input.read(); ch=(char)x; if(Character.isLetter(ch)&&InWord==false)//第一次讀取到字母 則讀取進入到單詞中 { InWord=true; str+=ch; } else if(!Character.isLetter(ch)&&InWord==true)//讀取到單詞中時 讀取到第一個非字母的字符 則讀完一個單詞 { if(!stopList.contains(str))//不在停用詞中 { count++; } InWord=false; str=""; } else if(InWord==true)//讀取字母在單詞中 { str+=ch; } if(x==-1) { break; } }
...
}

 

主函數及其類中方法如下:

主函數:得到用戶的指令(存儲在args中的字符串數組)調用analyseCommand()來處理

analyseCommand:首先處理命令是否有效,如參數應該為"-a"格式的字符串加字母形式;如果未文件名,文件是否存在;都滿足時則將參數給commandAction執行具體功能。用戶輸入的信息為字符串數組,我將其放入到了字符串集合的集合中,這里有點繞,目的是便於后面處理指令參數,用戶指令如 -c -w input.txt -e stop.txt -o output.txt,則將其分為三個字符串集合-c -w input.txt, -e stop.txt和-o output.txt,每個集合末尾裝的是文件名,每個集合中的命令是針對自己文件的。

commandAction:先讀取所有文件名,即遍歷所有集合,取出集合末尾的字符串,判斷其屬性分別給到input,output和stop變量中。然后再便利所有指令集合執行相應功能。

public class Main {
    public static void main(String[] args) throws FileNotFoundException, IOException 
    {        
        analyseCommand(args);
    }
    
    public static void analyseCommand(String[] args) throws IOException
    {    
        List<List<String>> commandsList =new ArrayList<>();
        List<String> commands=new ArrayList<>();
        if(args.length==0)//需要用戶輸出參數
        {
            System.out.println("Command codes are needed !");
        }
        else
        {
            for(int i=0;i<args.length;i++)
            {
                commands.add(args[i]);
                if(!args[i].matches("^-.*"))//不是命令符號
                {
                    if(args[i].contains("."))//是文件名或目錄
                    {
                        if(!new File(args[i]).exists())//文件不存在
                        {
                            System.out.println("The file named "+args[i]+" does not exist");
                            System.exit(0);
                        }
                        else
                        {
                            commandsList.add(commands);
                            commands=new ArrayList<>();
                        }
                    }
                    else//指令有錯
                    {
                        System.out.println("The "+(i+1)+"th code("+args[i]+") must begin with '-'");
                        System.exit(0);
                    }
                }
            }
        }
        commandAction(commandsList);
    }
    
    public static void commandAction(List<List<String>> commandList) throws IOException
    {    
        String input=null,output=null,stop=null;
        
        for(List<String> commands:commandList)
        {
            if(commands.contains("-o"))
            {
                output=commands.get(commands.size()-1);
            }
            else if(commands.contains("-e"))
            {
                stop=commands.get(commands.size()-1);
            }
            else
            {
                input=commands.get(commands.size()-1);
            }
        }
        
        WordCount wc=new WordCount(input,stop,output);
        
        for(List<String> commands:commandList)
        {
            for(int i=0;i<commands.size()-1;i++)
            {
                switch(commands.get(i))
                {
                    case "-c":wc.CountChars();
                    break;
                    case "-w":wc.CountWords();
                    break;
                    case "-l":wc.CountLines();
                    break;
                    case "-s":wc.CountLinesByKind();
                    break;
                    case "-e":wc.CountWordsWithLimite();
                    break;
                    case "-o":break;
                    default:System.out.println("No such command code");
                }
            }
        }
    }
}

 測試

1.初步功能測試

使用此函數的main類文件作為input后測試看預期結果是否相同

程序運行結果如下

 

結果符合預期,大體功能實現。

2.進一步測試

准備一個標准的類文件(包含足夠多的代碼,注釋等)

使用多種組合命令行

1).未指定輸出目錄的  默認為bin下的result.txt中

-c -w -l -s C:\Users\iy2524\Desktop\input.txt -e C:\Users\iy2524\Desktop\stop.txt

2).使用其他順序組合命令

-w -c -s -l  C:\Users\iy2524\Desktop\input.txt

...

3).輸入不存在的命令 或者錯誤的文件名

總結

初步完成了WordCount的基本功,在項目中,遇到了許多問題,都是細節的小問題,問題原因出在對許多方法的細節理解還不到位。通過網上查詢相關問題的解答信息,解決了絕大多數問題。項目開發中遇到問題時很常見的,問題有且會時多瑣碎復雜的。再遇到問題的時候要耐心分析,要學會使用調試工具,便於分析問題出現過程中的詳細信息。借助網絡可以解決絕大多數問題,不懂得東西要查,網上大佬多得是,類似的問題解決經驗也是有的,所以網絡通常是解決問題的第一求助目標,方便有效。在項目代碼中,規范使用代碼格式和注釋等,可以很方便理清代碼內容,不至於混亂,在尋找代碼分析代碼處浪費時間。編寫代碼不是拿到問題就動手干的,應該先分析問題解決方案,如何最優最有效快速解決問題才是關鍵,不要寫到一半時候才發現問題,到時候會浪費很多時間精力。問題自己獨立思考解決固然重要,但是也要考慮其他人的解決思路,如何更好的完成項目,是我們應該學習的地方,總結多人經驗,不要固步自封,只顧自己敲自己的代碼,畢竟個人能力有限,人多的力量更大。


免責聲明!

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



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