java中使用Files.readLines()處理文本中行數據方式

使用Files.readLines()處理文本中行數據

開發中遇到對數據庫導出到文件裡的數據進行處理,然後對處理後的數據再重新寫回文件中,在這個過程中使用到瞭Files.readLines()方法

/**
 *
 * @param file : existed file
 * @throws IOException
 */
public void lineProcess(File file) throws IOException {
    Files.readLines(file, Charset.defaultCharset(), new LineProcessor() {
        File outFile = new File("outfile");//處理後的數據輸出文件
        List<String> lines = new ArrayList<String>();
        @Override
        public boolean processLine(String line) throws IOException {
            String newLine = "";
            //file中的 line數據格式:name,age,address -> NAME,AGE,ADDRESS,
            String[] contents = line.split(",");
            for (int i=0;i<contents.length;i++){
                newLine.concat(contents[i].toLowerCase());
            }
            lines.add(newLine);
            //將處理後的數寫入新的文件 outFile
            FileUtils.writeLines(outFile,lines,true);
            lines.clear();
            return true;
        }
        @Override
        public Object getResult() {
            try{
                FileUtils.writeLines(outFile,lines,true);
            }catch (Exception e){
                e.getCause();
            }
            lines.clear();
            return null;
        }
    });
}

方法中的LineProcessor()實現對每一行數據處理邏輯。

依賴guava

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>

被readLine()折騰瞭一把

雖然寫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) { /* EOF */
      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”來進行數據處理。

在Java I/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()一定要註意

讀入的數據要註意有/r或/n或/r/n

沒有數據時會阻塞,在數據流異常或斷開時才會返回null

使用socket之類的數據流時,要避免使用readLine(),以免為瞭等待一個換行/回車符而一直阻塞

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: