Panduan untuk BufferedReader

1. Ikhtisar

BufferedReader adalah kelas yang menyederhanakan membaca teks dari aliran input karakter. Ini menyangga karakter untuk memungkinkan pembacaan data teks yang efisien.

Dalam tutorial ini, kita akan melihat bagaimana menggunakan kelas BufferedReader .

2. Kapan Menggunakan BufferedReader

Secara umum, BufferedReader berguna jika kita ingin membaca teks dari semua jenis sumber input apakah itu file, soket, atau yang lainnya.

Sederhananya, ini memungkinkan kami meminimalkan jumlah operasi I / O dengan membaca potongan karakter dan menyimpannya dalam buffer internal. Meskipun buffer memiliki data, pembaca akan membacanya, bukan langsung dari aliran yang mendasarinya.

2.1. Menyangga Pembaca Lain

Seperti kebanyakan kelas I / O Java, BufferedReader menerapkan pola Dekorator, yang berarti ia mengharapkan Pembaca dalam konstruktornya. Dengan cara ini, memungkinkan kami untuk secara fleksibel memperluas instance implementasi Reader dengan fungsionalitas buffering:

BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/input.txt"));

Tapi, jika buffering tidak penting bagi kita, kita bisa langsung menggunakan FileReader :

FileReader reader = new FileReader("src/main/resources/input.txt");

Selain buffering, BufferedReader juga menyediakan beberapa fungsi pembantu yang bagus untuk membaca file baris demi baris . Jadi, meskipun tampaknya lebih mudah menggunakan FileReader secara langsung, BufferedReader bisa sangat membantu.

2.2. Menyangga Arus

Secara umum, kita dapat mengkonfigurasi BufferedReader untuk mengambil aliran input apa punsebagai sumber yang mendasari . Kita bisa melakukannya menggunakan InputStreamReader dan membungkusnya di konstruktor:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

Dalam contoh di atas, kita membaca dari System.in yang biasanya sesuai dengan masukan dari keyboard. Demikian pula, kita bisa melewatkan aliran masukan untuk membaca dari soket, file atau jenis masukan tekstual yang bisa dibayangkan. Satu-satunya prasyarat adalah ada implementasi InputStream yang sesuai untuknya.

2.3. BufferedReader vs Scanner

Sebagai alternatif, kita bisa menggunakan kelas Scanner untuk mencapai fungsionalitas yang sama seperti BufferedReader.

Namun, ada perbedaan signifikan antara kedua kelas ini yang dapat membuatnya lebih atau kurang nyaman bagi kita, bergantung pada kasus penggunaan kita:

  • BufferedReader disinkronkan (thread-safe) sedangkan Scanner tidak
  • Pemindai dapat mengurai jenis dan string primitif menggunakan ekspresi reguler
  • BufferedReader memungkinkan untuk mengubah ukuran buffer sementara Scanner memiliki ukuran buffer tetap
  • BufferedReader memiliki ukuran buffer default yang lebih besar
  • Pemindai menyembunyikan IOException , sedangkan BufferedReader memaksa kita untuk menanganinya
  • BufferedReader biasanya lebih cepat daripada Scanner karena hanya membaca data tanpa menguraikannya

Dengan pemikiran ini, jika kita mengurai token individu dalam sebuah file, maka Scanner akan terasa sedikit lebih alami daripada BufferedReader. Tapi, hanya membaca baris pada satu waktu adalah tempat BufferedReader bersinar.

Jika perlu, kami juga memiliki panduan tentang Scanner juga.

3. Membaca Teks Dengan BufferedReader

Mari kita melalui seluruh proses membangun, menggunakan dan menghancurkan BufferReader dengan benar untuk membaca dari file teks.

3.1. Menginisialisasi BufferedReader

Pertama, mari buat BufferedReader menggunakan konstruktor BufferedReader (Reader) :

BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/input.txt"));

Membungkus FileReader seperti ini adalah cara yang bagus untuk menambahkan buffering sebagai aspek bagi pembaca lain.

Secara default, ini akan menggunakan buffer sebesar 8 KB. Namun, jika kita ingin menyangga blok yang lebih kecil atau lebih besar, kita dapat menggunakan konstruktor BufferedReader (Reader, int) :

BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/input.txt")), 16384);

Ini akan mengatur ukuran buffer menjadi 16384 byte (16 KB).

Ukuran buffer yang optimal bergantung pada faktor-faktor seperti jenis aliran input dan perangkat keras tempat kode dijalankan. Untuk alasan ini, untuk mencapai ukuran buffer yang ideal, kita harus menemukannya sendiri dengan bereksperimen.

Yang terbaik adalah menggunakan kekuatan 2 sebagai ukuran buffer karena sebagian besar perangkat keras memiliki kekuatan 2 sebagai ukuran blok.

Terakhir, ada satu cara praktis untuk membuat BufferedReader menggunakan kelas pembantu Files dari API java.nio :

BufferedReader reader = Files.newBufferedReader(Paths.get("src/main/resources/input.txt"))

Menciptakannyaseperti ini adalah cara yang bagus untuk buffer jika kita ingin membaca file karena kita tidak harus membuat FileReader secara manual terlebih dahulu lalu membungkusnya.

3.2. Membaca Baris demi Baris

Selanjutnya, mari kita membaca konten file menggunakan metode readLine :

public String readAllLines(BufferedReader reader) throws IOException { StringBuilder content = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { content.append(line); content.append(System.lineSeparator()); } return content.toString(); }

Kita dapat melakukan hal yang sama seperti di atas menggunakan metode garis yang diperkenalkan di Java 8 dengan lebih sederhana:

public String readAllLinesWithStream(BufferedReader reader) { return reader.lines() .collect(Collectors.joining(System.lineSeparator())); }

3.3. Menutup Arus

Setelah menggunakan BufferedReader , kita harus memanggil metode close () untuk melepaskan sumber daya sistem yang terkait dengannya. Ini dilakukan secara otomatis jika kita menggunakan blok coba-dengan-sumber daya :

try (BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/input.txt"))) { return readAllLines(reader); }

4. Metode Berguna Lainnya

Sekarang mari kita fokus pada berbagai metode berguna yang tersedia di BufferedReader.

4.1. Membaca Karakter Tunggal

Kita bisa menggunakan metode read () untuk membaca satu karakter. Mari membaca seluruh konten karakter demi karakter hingga akhir streaming:

public String readAllCharsOneByOne(BufferedReader reader) throws IOException { StringBuilder content = new StringBuilder(); int value; while ((value = reader.read()) != -1) { content.append((char) value); } return content.toString(); }

This will read the characters (returned as ASCII values), cast them to char and append them to the result. We repeat this until the end of the stream, which is indicated by the response value -1 from the read() method.

4.2. Reading Multiple Characters

If we want to read multiple characters at once, we can use the method read(char[] cbuf, int off, int len):

public String readMultipleChars(BufferedReader reader) throws IOException { int length; char[] chars = new char[length]; int charsRead = reader.read(chars, 0, length); String result; if (charsRead != -1) { result = new String(chars, 0, charsRead); } else { result = ""; } return result; }

In the above code example, we'll read up to 5 characters into a char array and construct a string from it. In the case that no characters were read in our read attempt (i.e. we've reached the end of the stream), we'll simply return an empty string.

4.3. Skipping Characters

We can also skip a given number of characters by calling the skip(long n) method:

@Test public void givenBufferedReader_whensSkipChars_thenOk() throws IOException { StringBuilder result = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new StringReader("1__2__3__4__5"))) { int value; while ((value = reader.read()) != -1) { result.append((char) value); reader.skip(2L); } } assertEquals("12345", result); }

In the above example, we read from an input string which contains numbers separated by two underscores. In order to construct a string containing only the numbers, we are skipping the underscores by calling the skip method.

4.4. mark and reset

We can use the mark(int readAheadLimit) and reset() methods to mark some position in the stream and return to it later. As a somewhat contrived example, let's use mark() and reset() to ignore all whitespaces at the beginning of a stream:

@Test public void givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk() throws IOException { String result; try (BufferedReader reader = new BufferedReader(new StringReader(" Lorem ipsum dolor sit amet."))) { do { reader.mark(1); } while(Character.isWhitespace(reader.read())) reader.reset(); result = reader.readLine(); } assertEquals("Lorem ipsum dolor sit amet.", result); }

In the above example, we use the mark() method to mark the position we just read. Giving it a value of 1 means only the code will remember the mark for one character forward. It's handy here because, once we see our first non-whitespace character, we can go back and re-read that character without needing to reprocess the whole stream. Without having a mark, we'd lose the L in our final string.

Note that because mark() can throw an UnsupportedOperationException, it's pretty common to associate markSupported() with code that invokes mark(). Though, we don't actually need it here. That's because markSupported() always returns true for BufferedReader.

Of course, we might be able to do the above a bit more elegantly in other ways, and indeed mark and reset aren't very typical methods. They certainly come in handy, though, when there is a need to look ahead.

5. Conclusion

Dalam tutorial singkat ini, kita telah mempelajari cara membaca aliran input karakter pada contoh praktis menggunakan BufferedReader .

Terakhir, kode sumber untuk contoh tersedia di Github.