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()來了。
發資料:
- BufferedWriter output = new BufferedWriter(new OutputStreamWriter(conn.openOutputStream()));
- int i = 1;
- String message = "message " + i;
- while(isRunning) {
- output.write(message+"/n");
- i++;
- }
讀資料:
- BufferedReader input = new BufferedReader(new InputStreamReader(m_conn.openInputStream()));
- String message = "";
- String line = null;
- while((line = m_input.readLine()) != null) {
- message += line;
- }
- 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源碼摘出來的):
- String readLine(boolean ignoreLF) throws IOException {
- StringBuffer s = null;
- int startChar;
- synchronized (lock) {
- ensureOpen();
- boolean omitLF = ignoreLF || skipLF;
- bufferLoop:
- for (;;) {
- if (nextChar >= nChars)
- fill(); //在此讀資料
- if (nextChar >= nChars) {
- if (s != null && s.length() > 0)
- return s.toString();
- else
- return null;
- }
- ......//其它
- }
- private void fill() throws IOException {
- ..../其它
- int n;
- do {
- n = in.read(cb, dst, cb.length - dst); //實質
- } while (n == 0);
- if (n > 0) {
- nChars = dst + n;
- nextChar = dst;
- }
- }
從上面看出,readLine()是調用了read(char[] cbuf, int off, int len) 來讀取資料,後面再根據"/r"或"/n"來進行資料處理。
小結,使用readLine()一定要注意:
- 讀入的資料要注意有/r或/n或/r/n
- 沒有資料時會阻塞,在資料流異常或斷開時才會傳回null
- 使用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