天天看點

Java:java中BufferedReader的read()及readLine()方法的使用心得

BufferedReader的readLine()方法是阻塞式的, 如果到達流末尾, 就傳回null, 但如果client的socket末經關閉就銷毀, 則會産生IO異常. 正常的方法就是使用socket.close()關閉不需要的socket.

從一個有若幹行的檔案中依次讀取各行,處理後輸出,如果用以下方法,則會出現除第一行外行首字元丢失現象.

String str  = null;

br=new BufferedReader(new FileReader(fileName));

do{

  str = buf.readLine()); 

}while(br.read()!=-1);

以下用法會使每行都少首字元

while(br.read() != -1){

       str = br.readLine();     

 }

原因就在于br.read() != -1 這判斷條件上。 因為在執行這個條件的時候其實它已經讀取了一個字元了,然而在這裡并沒有對讀取出來的這個字元做處理,是以會出現少一個字元,如果你這裡寫的是while(br.readLine()!=null)會出現隔一行少一行!

建議使用以下方法

String str = null;

      while((str = br.readLine()) != null){

      //System.out.println(str);//此時str就儲存了一行字元串

}

這樣應該就可以無字元丢失地得到一行了

雖然寫IO方面的程式不多,但BufferedReader/BufferedInputStream倒是用過好幾次的,原因是:

  • 它有一個很特别的方法:readLine(),使用起來特别友善,每次讀回來的都是一行,省了很多手動拼接buffer的瑣碎;
  • 它比較高效,相對于一個字元/位元組地讀取、轉換、傳回來說,它有一個緩沖區,讀滿緩沖區才傳回;一般情況下,都建議使用它們把其它Reader/InputStream包起來,使得讀取資料更高效。
  • 對于檔案來說,經常遇到一行一行的,特别相符情景。

這次是在藍牙開發時,使用兩個藍牙互相傳資料(即一個發一個收),bluecove這個開源元件已經把資料讀取都封裝成InputStream了,也就相當于平時的IO讀取了,很自然就使用起readLine()來了。

發資料:

  1. BufferedWriter output = new BufferedWriter(new OutputStreamWriter(conn.openOutputStream()));   
  2. int i = 1;  
  3. String message = "message " + i;  
  4. while(isRunning) {  
  5.     output.write(message+"/n");   
  6.     i++;  
  7. }  

讀資料:

  1. BufferedReader input = new BufferedReader(new  InputStreamReader(m_conn.openInputStream()));  
  2. String message = "";  
  3. String line = null;  
  4. while((line = m_input.readLine()) != null) {  
  5.     message += line;  
  6. }  
  7. System.out.println(message);  

上面是代碼的節選,使用這段代碼會發現寫資料時每次都成功,而讀資料側卻一直沒有資料輸出(除非把流關掉)。經過折騰,原來這裡面有幾個大問題需要了解:

  • 誤以為readLine()是讀取到沒有資料時就傳回null(因為其它read方法當讀到沒有資料時傳回-1),而實際上readLine()是一個阻塞函數,當沒有資料讀取時,就一直會阻塞在那,而不是傳回null;因為readLine()阻塞後,System.out.println(message)這句根本就不會執行到,是以在接收端就不會有東西輸出。要想執行到System.out.println(message),一個辦法是發送完資料後就關掉流,這樣readLine()結束阻塞狀态,而能夠得到正确的結果,但顯然不能傳一行就關一次資料流;另外一個辦法是把System.out.println(message)放到while循環體内就可以。
  • readLine()隻有在資料流發生異常或者另一端被close()掉時,才會傳回null值。
  • 如果不指定buffer大小,則readLine()使用的buffer有8192個字元。在達到buffer大小之前,隻有遇到"/r"、"/n"、"/r/n"才會傳回。

readLine()的實質(下面是從JDK源碼摘出來的):

  1. String readLine(boolean ignoreLF) throws IOException {  
  2.     StringBuffer s = null;  
  3.     int startChar;  
  4.         synchronized (lock) {  
  5.             ensureOpen();  
  6.         boolean omitLF = ignoreLF || skipLF;  
  7.         bufferLoop:  
  8.         for (;;) {  
  9.         if (nextChar >= nChars)  
  10.             fill(); //在此讀資料  
  11.         if (nextChar >= nChars) {   
  12.             if (s != null && s.length() > 0)  
  13.             return s.toString();  
  14.             else  
  15.             return null;  
  16.         }  
  17.       ......//其它  
  18. }  
  19. private void fill() throws IOException {  
  20.     ..../其它  
  21.     int n;  
  22.     do {  
  23.         n = in.read(cb, dst, cb.length - dst); //實質  
  24.     } while (n == 0);  
  25.     if (n > 0) {  
  26.         nChars = dst + n;  
  27.         nextChar = dst;  
  28.     }  
  29.     }  

從上面看出,readLine()是調用了read(char[] cbuf, int off, int len) 來讀取資料,後面再根據"/r"或"/n"來進行資料處理。

小結,使用readLine()一定要注意:

  1. 讀入的資料要注意有/r或/n或/r/n
  2. 沒有資料時會阻塞,在資料流異常或斷開時才會傳回null
  3. 使用socket之類的資料流時,要避免使用readLine(),以免為了等待一個換行/回車符而一直阻塞

1.讀取一個txt檔案,方法很多種我使用了字元流來讀取(為了友善)

  FileReader fr = new FileReader("f:\\TestJava.Java");

   BufferedReader bf = new BufferedReader(fr);

//這裡進行讀取

int b;

   while((b=bf.read())!=-1){

    System.out.println(bf.readLine());

   }

發現每行的第一個字元都沒有顯示出來,原因呢:b=bf.read())!=-1  每次都會先讀取一個位元組出來,是以後面的bf.readLine())讀取的就會每行少一個位元組,是以,應該使用

String valueString = null;

   while ((valueString=bf.readLine())!=null){

    System.out.println(valueString);

   }

轉載于:https://my.oschina.net/u/3624220/blog/1528602