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.