今天在作做業的時候碰到了一個問題:要用java作詞頻統計,可是這就犯難了,java如何有選擇性的進行文件輸入輸出呢?java
查閱文檔可知,inputStream類和outputStream類是字節流輸入輸出,而reader 類和writer 類則是直接Unicode碼輸入輸出,我爲了圖省事,我最後使用了FileReader類的read方法進行文件讀取。FileReader類中的read方法能夠逐字符讀取,返回類型爲int,其實就是0~255的char 類型,只要進行強制類型轉換,就能夠獲得讀入的字符,當遇到文件尾的時候,該方法返回-1。由於是char類型,因此若是遇到中文字符,好比句號,就會按照兩個char處理。正則表達式
接下來解決選擇性輸入的問題。思路很簡單:app
1.若是如今所讀取到的字符不是咱們想要的,那麼就一直向下讀取,直到讀取到咱們想要的函數
2.若是遇到了返回值爲-1的狀況,退出,表示文件讀取完成(這一步必定要放在中間判斷)spa
3.若是如今讀取的是咱們想要的,就一直讀取,直到出現咱們不須要的,退出,等待下一次讀取。code
解釋一下第二個爲何要在中間判斷吧。blog
由於若是放在三個判斷的第一個,那麼若是文件以無用的字符結尾,則最後一個單詞輸出後還會多餘輸出一個換行,由於return了一個空的StringBuffer,而輸出語句用的是println。文檔
若是放在三個判斷中的最後,那麼若是文件以有用單詞結尾,則最後一個單詞沒法輸出,由於掃描到單詞後緊接着就是read返回-1,此時就直接返回「字符串完成」,因此少最後一個單詞字符串
第一次解決的時候,由於判斷1的位置和判斷3混在了一塊兒,因此出現了最後一個單詞不能輸出的狀況。input
錯誤寫法我也寫下來吧,以避免之後再錯:
錯誤1——放在最前面:
private String read() throws IOException{ StringBuffer strBuf = new StringBuffer(); int t=fileReader.read(); if(t==-1) return "\\$";
錯誤2——放在最後:
1 while(t!=-1&&isAlph((char)t)){ 2 strBuf.append((char) t); 3 t=fileReader.read(); 4 } 5 if(t==-1) return "\\$"; 6 return strBuf.toString(); 7 }
正確作法:(徹底代碼,而且簡化了不須要的流程)
1 private String read() throws IOException{ 2 StringBuffer strBuf = new StringBuffer(); 3 int t=fileReader.read(); 4 if(t==-1) return "\\$"; 5 while(t!=-1&&isAlph((char)t)){ 6 strBuf.append((char) t); 7 t=fileReader.read(); 8 } 9 return strBuf.toString(); 10 }
所以,能夠看到代碼的細節也是不容忽視的,同一句話,不一樣位置,致使結果截然不同。
======================================我是小清新的分割線=========================================
2016.5.8更新
嗯,好吧,我又來啦,此次的java之旅差很少就已經完成啦,如今讓咱們梳理一下此次學到的輸入流的組合方式:
除了上文中逐個字符進行輸入的方法以外,對於有些問題,正則表達式可能更加好用。
好比咱們想逐行讀取文本怎麼辦呢,咱們會發現FileReader類中是沒有這種方法的,所以咱們須要把File類和其餘輸入流組合起來,自力更生。好比咱們把File和Scanner類結合起來,熟悉控制檯的小夥伴們必定很清楚,Scanner經常使用來進行控制檯輸入,而且Scanner能夠一次讀取一行,所以,咱們就選它啦。
所以我麼能夠愉快地一次一行進行操做啦
定義以下:(in 和 out 分別是輸入流和輸出流)
public Tree_T_Display(File fin,File fout) throws IOException { in = new Scanner(new FileInputStream(fin)); out = new FileWriter(fout); }
這樣read()函數就能夠這樣定義啦:
public String read()throws IOException{ if(in.hasNext()){ String s = in.nextLine(); s=s.replaceAll(" ", "");//去掉空格 return s; } return null; }
這樣一來,配合正則表達式,咱們的代碼量能夠減小,而且在過濾數據方面也能夠交給正則表達式去作。
另外,正則表達式也提一下吧,在java的正則表達式我本身用過的有兩種調用方法,一種是String類中的split,matches方法,注意split方法中匹配上的字符不會出如今新的字符串中,而matches方法中的正則必須和字符串徹底匹配,不然就返回false。
另外,正則中,+表示1~n個(n)不限制,*表示0~n個,和{1,} {0,}分別等價。
^表示行開頭,$表示行結尾,^在[]中表示「非」,取補集
舉例以下
目標:匹配「單詞」開頭+「(」結尾的字符串:
1 s.matches("^[a-zA-Z]+[^\\(]$")
匹配可能有特殊字符的單詞,縮寫等:
1 s.matches("^[^ ]+$")
這裏就用到了^在[]中的狀況,一個[]是一個可匹配的類,上面第二行做用是匹配一個「以非空格開頭而且以之爲結尾的字符串」
pte String read() throws IOException{
StringBuffer strBuf = new StringBuffer();
int t=fileReader.read();
if(t==-1) return "\\$";
while(t!=-1&&isAlph((char)t)){
strBuf.append((char) t);
t=fileReader.read();
}
//保留最大單詞長度,爲格式化輸出作準備
maxLen = maxLen < strBuf.length() ? strBuf.length() : maxLen;
return strBuf.toString();}