優(yōu)雅的按行讀取大文件

在文件比較大時(shí),我們不能一次將整個(gè)文件讀取到內(nèi)存中,需要按行讀并按行處理,但是這樣帶來(lái)的問(wèn)題在于將讀取文件的代碼與處理數(shù)據(jù)的代碼混在了一起:


    try (BufferedReader in = new BufferedReader(new FileReader(file))) {
      String line;
      while ((line = in.readLine()) != null) {
        
        doProcessLine(line);
        
        if (!doNext(line)) {//省略
          break;
        }
      }
    }
 

可以使用Guava將它分開,例如:

CharStreams.readLine(reader, new LineProcessor<T>() {
  public boolean processLine(String line) throws IOException {
      if (!doNext(line)) {
          return false;//不需要再繼續(xù)讀
      }
      ...
  }
  public T getResult() {
      return xxx;
  }
});

這樣代碼比較繁瑣,不自然。
更自然的方式:

for (String line : IOStreams.lines(reader)) {
    if (!doNext(line)) {
        break;//停止讀,因?yàn)槭莑azy的,當(dāng)前行處理結(jié)束前,下一行不會(huì)被讀入到內(nèi)存
    }
    ...
}

實(shí)現(xiàn)方式:
先定義兩個(gè)基礎(chǔ)接口CloseableIterable和CloseableIterator:

import java.io.Closeable;

/**
 * 可被關(guān)閉的{@link Iterable}對(duì)象,可用于基于某種資源(如ResultSet)的迭代。
 * <p>
 * <pre>
 *     {@code
 *          try (CloseableIterable<E> iter = xxx) {
 *              for (E elem : iter) {
 *                  ……
 *              }
 *          }
 *     }
 * </pre>
 *
 * @author gaohang on 15/9/21.
 */
public interface CloseableIterable<E> extends Iterable<E>, Closeable {

  /**
   * 關(guān)閉資源
   */
  void close();
}

import java.io.Closeable;
import java.util.Iterator;

/**
 * @author gaohang on 15/9/21.
 * @see CloseableIterable
 */
public interface CloseableIterator<E> extends Iterator<E>, Closeable {
  void close();
}

最后實(shí)現(xiàn)IOStreams類:

import cn.yxffcode.freetookit.collection.ImmutableIterator;
import cn.yxffcode.freetookit.lang.CloseableIterable;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 輸入輸出流相關(guān)的工具
 *
 * @author gaohang on 15/10/23.
 */
public final class IOStreams {
  private IOStreams() {
  }

  /**
   * 按行讀取
   * <p>
   * 使用lazy的讀,只有在返回的Iterable對(duì)象上迭代一次才會(huì)讀一行
   * <p>
   * 當(dāng)讀取的文件比較大時(shí),不合適一次將事個(gè)文件的行讀入內(nèi)存,需要逐行讀并處理。在使用guava的CharStreams時(shí)比較繁鎖,例如:
   * <pre>
   *      CharStreams.readLine(reader, new LineProcessor<T>() {
   *          public boolean processLine(String line) throws IOException {
   *              if (line ....) {
   *                  return false;//不需要再繼續(xù)讀
   *              }
   *              ...
   *          }
   *          public T getResult() {
   *              return xxx;
   *          }
   *      });
   * </pre>
   * 換成此方法,代碼如下:
   * <pre>
   *     for (String line : IOStreams.lines(reader)) {
   *         if (line...) {
   *             break;//停止讀,因?yàn)槭莑azy的,當(dāng)前行處理結(jié)束前,下一行不會(huì)被讀入到內(nèi)存
   *         }
   *         ...
   *     }
   * </pre>
   */
  public static Iterable<String> lines(final BufferedReader reader) {
    checkNotNull(reader);
    return new Iterable<String>() {
      @Override public Iterator<String> iterator() {
        return new ImmutableIterator<String>() {

          private String line;

          @Override public boolean hasNext() {
            try {
              return (line = reader.readLine()) != null;
            } catch (IOException e) {
              throw new IOReaderException(e);
            }
          }

          @Override public String next() {
            return line;
          }
        };
      }
    };
  }

  public static CloseableIterable<String> lines(final File src) {

    return new CloseableIterable<String>() {

      private BufferedReader in;

      @Override public void close() {
        if (in != null) {
          try {
            in.close();
          } catch (IOException e) {
            throw new IOReaderException(e);
          }
        }
      }

      @Override public Iterator<String> iterator() {
        try {
          in = new BufferedReader(new FileReader(src));
          return lines(in).iterator();
        } catch (FileNotFoundException e) {
          throw new IOReaderException(e);
        }
      }
    };
  }

  public static BufferedReader toBufferedReader(InputStream in) {
    return new BufferedReader(new InputStreamReader(in));
  }

  public static BufferedReader openClasspath(String classpath) {
    return toBufferedReader(IOStreams.class.getResourceAsStream(classpath));
  }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,699評(píng)論 1 32
  • 一、Python簡(jiǎn)介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡(jiǎn)介】: Python 是一個(gè)...
    _小老虎_閱讀 6,363評(píng)論 0 10
  • 在窗外謀劃了一夜的雪 先下在 朋友圈了
    無(wú)所事事的盲從閱讀 627評(píng)論 1 0
  • 獻(xiàn)給你愛的人 益農(nóng)(原創(chuàng)) 春天又一次來(lái)到我們身旁 觸手可及的風(fēng) 暖暖地?fù)崦?也暖暖地圍繞著你 讓我們一起 靜...
    在淵閱讀 545評(píng)論 0 2
  • sadasds
    Mayson7閱讀 144評(píng)論 0 0

友情鏈接更多精彩內(nèi)容