Cara Membaca Fail Besar dengan Java dengan cekap

1. Gambaran keseluruhan

Tutorial ini akan menunjukkan cara membaca semua baris dari fail besar di Java dengan cara yang cekap.

Artikel ini adalah bagian dari tutorial " Java - Back to Basic " di Baeldung.

2. Membaca dalam Ingatan

Cara standard membaca baris fail adalah dalam ingatan - Guava dan Apache Commons IO menyediakan cara cepat untuk melakukan itu:

Files.readLines(new File(path), Charsets.UTF_8);
FileUtils.readLines(new File(path));

Masalah dengan pendekatan ini adalah bahawa semua baris fail disimpan dalam memori - yang akan dengan cepat membawa kepada OutOfMemoryError jika Fail cukup besar.

Contohnya - membaca fail ~ 1Gb :

@Test public void givenUsingGuava_whenIteratingAFile_thenWorks() throws IOException { String path = ... Files.readLines(new File(path), Charsets.UTF_8); }

Ini bermula dengan sedikit memori yang habis: (~ 0 Mb habis)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 128 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 116 Mb

Namun, setelah fail penuh diproses , kita ada di akhir: (~ 2 Gb habis)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 2666 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 490 Mb

Yang bermaksud bahawa kira-kira 2,1 Gb memori habis oleh proses - alasannya mudah - baris fail semuanya disimpan dalam memori sekarang.

Seharusnya jelas pada saat ini bahawa menyimpan memori kandungan fail akan menghabiskan memori yang tersedia dengan cepat - tanpa mengira berapa sebenarnya fail itu.

Lebih-lebih lagi, kita biasanya tidak memerlukan semua baris dalam fail dalam memori sekaligus - sebaliknya, kita hanya perlu melakukan lelaran setiap satu, melakukan pemprosesan dan membuangnya. Oleh itu, inilah yang akan kita lakukan - berulang kali melalui garis tanpa menyimpan semuanya dalam ingatan.

3. Streaming Melalui Fail

Sekarang mari kita cari penyelesaiannya - kita akan menggunakan java.util.Scanner untuk menjalankan kandungan fail dan mengambil baris secara bersiri, satu persatu:

FileInputStream inputStream = null; Scanner sc = null; try { inputStream = new FileInputStream(path); sc = new Scanner(inputStream, "UTF-8"); while (sc.hasNextLine()) { String line = sc.nextLine(); // System.out.println(line); } // note that Scanner suppresses exceptions if (sc.ioException() != null) { throw sc.ioException(); } } finally { if (inputStream != null) { inputStream.close(); } if (sc != null) { sc.close(); } }

Penyelesaian ini akan berulang melalui semua baris dalam fail - memungkinkan untuk memproses setiap baris - tanpa menyimpan rujukan kepada mereka - dan kesimpulannya, tanpa menyimpannya dalam ingatan : (~ 150 Mb habis)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 763 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 605 Mb

4. Streaming Dengan Apache Commons IO

Perkara yang sama dapat dicapai dengan menggunakan perpustakaan Commons IO juga, dengan menggunakan LineIterator khusus yang disediakan oleh perpustakaan:

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8"); try { while (it.hasNext()) { String line = it.nextLine(); // do something with line } } finally { LineIterator.closeQuietly(it); }

Oleh kerana keseluruhan fail tidak ada dalam memori - ini juga akan menghasilkan bilangan penggunaan memori yang cukup konservatif : (~ 150 Mb habis)

[main] INFO o.b.java.CoreJavaIoIntegrationTest - Total Memory: 752 Mb [main] INFO o.b.java.CoreJavaIoIntegrationTest - Free Memory: 564 Mb

5. Kesimpulan

Artikel ringkas ini menunjukkan cara memproses baris dalam fail besar tanpa berulang, tanpa menghabiskan memori yang ada - yang terbukti cukup berguna ketika bekerja dengan fail besar ini.

Pelaksanaan semua contoh dan coretan kod ini terdapat di projek GitHub kami - ini adalah projek berasaskan Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.