web-dev-qa-db-ja.com

Javaで大きなファイルを読み込む

Javaとメモリの問題をよく知っている人からのアドバイスが必要です。大きなファイル(1.5GBのようなもの)があり、このファイルを多く(100の小さなファイル例)小さいファイル。

私は一般的にそれを行う方法を知っています(BufferedReaderを使用)が、メモリに関するアドバイス、またはそれをより速く行うためのヒントがあれば教えてください。

ファイルにテキストが含まれていますが、バイナリではなく、1行に約20文字あります。

59
CC.

まず、ファイルにバイナリデータが含まれている場合、BufferedReaderを使用すると大きな間違いになります(データを文字列に変換するため、これは不要であり、データを簡単に破損する可能性があるため)。代わりにBufferedInputStreamを使用する必要があります。テキストデータであり、改行に沿って分割する必要がある場合は、BufferedReaderを使用しても問題ありません(ファイルに適切な長さの行が含まれていると仮定します)。

メモリに関しては、適切なサイズのバッファを使用しても問題はありません(少なくとも1MBを使用して、HDがほぼ順次読み取りと書き込みを行うようにします)。

速度が問題であることが判明した場合は、Java.nioパッケージ-それらはおそらくJava.io

28

メモリを節約するために、メモリにデータを不必要に保存/複製しないでください(つまり、ループ外の変数にデータを割り当てないでください)。入力が入ったらすぐに出力を処理するだけですimmediately.

BufferedReaderを使用しているかどうかは関係ありません。暗示的に示唆されているように、大幅に多くのメモリを消費することはありません。せいぜいパフォーマンスから数%しかヒットしません。 NIOの使用にも同じことが当てはまります。メモリの使用ではなく、スケーラビリティのみが向上します。同じファイルで何百ものスレッドを実行している場合にのみ興味深いものになります。

ファイルをループし、読み込んだらすぐにすべての行を他のファイルに書き込み、行をカウントし、100に達したら、次のファイルに切り替えます。

キックオフの例:

String encoding = "UTF-8";
int maxlines = 100;
BufferedReader reader = null;
BufferedWriter writer = null;

try {
    reader = new BufferedReader(new InputStreamReader(new FileInputStream("/bigfile.txt"), encoding));
    int count = 0;
    for (String line; (line = reader.readLine()) != null;) {
        if (count++ % maxlines == 0) {
            close(writer);
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/smallfile" + (count / maxlines) + ".txt"), encoding));
        }
        writer.write(line);
        writer.newLine();
    }
} finally {
    close(writer);
    close(reader);
}
32
BalusC

FileChannel sを使用して、メモリマップファイルの使用を検討できます。

通常、大きなファイルの場合はalotより高速です。 couldによりパフォーマンスが低下するため、パフォーマンスが低下するため、YMMVを使用します。

関連する回答: Java NIO FileChannelとFileOutputstreamのパフォーマンス/有用性

12
Ryan Emerle

Javaで行う必要がありますか?つまりプラットフォームに依存しない必要がありますか?そうでない場合は、* nixで「 split 」コマンドを使用することをお勧めします。本当に必要な場合は、Javaプログラムを介してこのコマンドを実行できます。私はテストしていませんが、どのようなJava IO実装よりも速く実行できると思います。

4
Mike

これは非常に良い記事です。 http://Java.Sun.com/developer/technicalArticles/Programming/PerfTuning/

要約すると、優れたパフォーマンスを得るには、次のことを行う必要があります。

  1. ディスクへのアクセスを避けます。
  2. 基盤となるオペレーティングシステムにはアクセスしないでください。
  3. メソッド呼び出しを避けてください。
  4. バイトと文字を個別に処理することは避けてください。

たとえば、ディスクへのアクセスを減らすために、大きなバッファを使用できます。この記事では、さまざまなアプローチについて説明します。

4
b.roth
package all.is.well;
import Java.io.IOException;
import Java.io.RandomAccessFile;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import junit.framework.TestCase;

/**
 * @author Naresh Bhabat
 * 
Following  implementation helps to deal with extra large files in Java.
This program is tested for dealing with 2GB input file.
There are some points where extra logic can be added in future.


Pleasenote: if we want to deal with binary input file, then instead of reading line,we need to read bytes from read file object.



It uses random access file,which is almost like streaming API.


 * ****************************************
Notes regarding executor framework and its readings.
Please note :ExecutorService executor = Executors.newFixedThreadPool(10);

 *         for 10 threads:Total time required for reading and writing the text in
 *         :seconds 349.317
 * 
 *         For 100:Total time required for reading the text and writing   : seconds 464.042
 * 
 *         For 1000 : Total time required for reading and writing text :466.538 
 *         For 10000  Total time required for reading and writing in seconds 479.701
 *
 * 
 */
public class DealWithHugeRecordsinFile extends TestCase {

        static final String FILEPATH = "C:\\springbatch\\bigfile1.txt.txt";
        static final String FILEPATH_WRITE = "C:\\springbatch\\writinghere.txt";
        static volatile RandomAccessFile fileToWrite;
        static volatile RandomAccessFile file;
        static volatile String fileContentsIter;
        static volatile int position = 0;

        public static void main(String[] args) throws IOException, InterruptedException {
                long currentTimeMillis = System.currentTimeMillis();

                try {
                        fileToWrite = new RandomAccessFile(FILEPATH_WRITE, "rw");//for random write,independent of thread obstacles 
                        file = new RandomAccessFile(FILEPATH, "r");//for random read,independent of thread obstacles 
                        seriouslyReadProcessAndWriteAsynch();

                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                Thread currentThread = Thread.currentThread();
                System.out.println(currentThread.getName());
                long currentTimeMillis2 = System.currentTimeMillis();
                double time_seconds = (currentTimeMillis2 - currentTimeMillis) / 1000.0;
                System.out.println("Total time required for reading the text in seconds " + time_seconds);

        }

        /**
         * @throws IOException
         * Something  asynchronously serious
         */
        public static void seriouslyReadProcessAndWriteAsynch() throws IOException {
                ExecutorService executor = Executors.newFixedThreadPool(10);//pls see for explanation in comments section of the class
                while (true) {
                        String readLine = file.readLine();
                        if (readLine == null) {
                                break;
                        }
                        Runnable genuineWorker = new Runnable() {
                                @Override
                                public void run() {
                                        // do hard processing here in this thread,i have consumed
                                        // some time and ignore some exception in write method.
                                        writeToFile(FILEPATH_WRITE, readLine);
                                        // System.out.println(" :" +
                                        // Thread.currentThread().getName());

                                }
                        };
                        executor.execute(genuineWorker);
                }
                executor.shutdown();
                while (!executor.isTerminated()) {
                }
                System.out.println("Finished all threads");
                file.close();
                fileToWrite.close();
        }

        /**
         * @param filePath
         * @param data
         * @param position
         */
        private static void writeToFile(String filePath, String data) {
                try {
                        // fileToWrite.seek(position);
                        data = "\n" + data;
                        if (!data.contains("Randomization")) {
                                return;
                        }
                        System.out.println("Let us do something time consuming to make this thread busy"+(position++) + "   :" + data);
                        System.out.println("Lets consume through this loop");
                        int i=1000;
                        while(i>0){
                        
                                i--;
                        }
                        fileToWrite.write(data.getBytes());
                        throw new Exception();
                } catch (Exception exception) {
                        System.out.println("exception was thrown but still we are able to proceeed further"
                                        + " \n This can be used for marking failure of the records");
                        //exception.printStackTrace();

                }

        }
}
1
RAM

従来の入出力ストリームよりも高速なJava.nioを使用できます。

http://Java.Sun.com/javase/6/docs/technotes/guides/io/index.html

1
Kartoch

はい。また、read(Char []、int init、int end)のような引数でread()を使用することは、そのような大きなファイルを読み取るためのより良い方法だと思います(例:read(buffer、0、buffer.length))

また、バイナリデータ入力ストリームにBufferedInputStreamReaderの代わりにBufferedReaderを使用すると、値が欠落するという問題も発生しました。したがって、このような場合には、BufferedInputStreamReaderを使用する方がはるかに優れています。

1
Namalak

行ごとに読み込むのではなく、誤って入力ファイル全体を読み込まない限り、主な制限はディスク速度です。 100行を含むファイルから始めて、1行ずつ100の異なるファイルに書き込み、現在のファイルに書き込まれた行数でトリガーメカニズムを動作させたい場合があります。そのプログラムは、状況に合わせて簡単に拡張できます。

引数なしでreadを使用しないでください。とても遅いです。バッファに読み込み、ファイルにすばやく移動することをお勧めします。

バイナリ読み取りをサポートしているため、bufferedInputStreamを使用します。

そしてそれだけです。

0
oneat