1. 程式人生 > >關於java中BufferedReader的read()及readLine()方法的使用心得

關於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()來了。

發資料:

[java] view plaincopy

  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原始碼摘出來的):

[java] view plaincopy

  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) { /* EOF */  
  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"來進行資料處理。

在JavaI/O書上也說了:

public String readLine() throws IOException
This method returns a string that contains a line of text from a text file. /r, /n, and /r/n are assumed to be line breaks and are not included in the returned string. This method is often used when reading user input from System.in, since most platforms only send the user's input to the running program after the user has typed a full line (that is, hit the Return key).
readLine() has the same problem with line ends that DataInputStream's readLine() method has; that is, the potential to hang on a lone carriage return that ends the stream . This problem is especially acute on networked connections, where readLine() should never be used.

小結,使用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);
   }