java如何有選擇的輸入多行文本
今天在做作業的時候碰到了一個問題:要用java做詞頻統計,但是這就犯難了,java如何有選擇性的進行文件輸入輸出呢?
查閱文檔可知,inputStream類和outputStream類是字節流輸入輸出,而reader 類和writer 類則是直接Unicode碼輸入輸出,我為了圖省事,我最后使用了FileReader類的read方法進行文件讀取。FileReader類中的read方法可以逐字符讀取,返回類型為int,其實就是0~255的char 類型,只要進行強制類型轉換,就可以得到讀入的字符,當遇到文件尾的時候,該方法返回-1。因為是char類型,所以如果遇到中文字符,比如句號,就會按照兩個char處理。
接下來解決選擇性輸入的問題。思路很簡單:
1.如果現在所讀取到的字符不是我們想要的,那么就一直向下讀取,直到讀取到我們想要的
2.如果遇到了返回值為-1的情況,退出,表示文件讀取完成(這一步一定要放在中間判斷)
3.如果現在讀取的是我們想要的,就一直讀取,直到出現我們不需要的,退出,等待下一次讀取。
解釋一下第二個為什么要在中間判斷吧。
因為如果放在三個判斷的第一個,那么如果文件以無用的字符結尾,則最后一個單詞輸出后還會多余輸出一個換行,因為return了一個空的StringBuffer,而輸出語句用的是println。
如果放在三個判斷中的最后,那么如果文件以有用單詞結尾,則最后一個單詞無法輸出,因為掃描到單詞后緊接着就是read返回-1,此時就直接返回“字符串完成”,所以少最后一個單詞
第一次解決的時候,因為判斷1的位置和判斷3混在了一起,所以出現了最后一個單詞不能輸出的情況。
錯誤寫法我也寫下來吧,以免以后再錯:
錯誤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();
}