CodeGym /Courses /Java Collections /BufferedReader, BufferedWriter

BufferedReader, BufferedWriter

Java Collections
Level 2 , Lesson 5
Available

"It's me again."

"Hi, Ellie!"

"Today I'd like to tell you all about BufferedReader and BufferedWriter."

"You already told me about them. They really aren't that complicated."

"OK. Then tell me how BufferedReader works."

"BufferedReader is like a 110/220V converter."

"You have to pass to the BufferedReader constructor the Reader object that data will be read from. A BufferedReader object reads big chunks of data from the Reader and stores them internally in a buffer. That's why using a BufferedReader to read from a Reader is faster than reading directly from a Reader."

"That's right. And what about BufferedWriter?"

"It's a piece of cake. Suppose we write to a FileWriter. The data is written to disk immediately. If we write little bits of data frequently, then we'll hit the disk a lot, which will slow down the program a lot. But if we use a BufferedWriter as a 'converter', then the write operation is much faster. When you write to a BufferedWriter, it saves the data in an internal buffer. When the buffer is full, it writes the data to the Writer as a single large chunk. This is much faster."

"Hmm. Spot on. But what have you forgotten?"

"After you're done writing, you need to call the flush() method on the BufferedWriter object to force it to send any data still in the buffer to the Writer."

"And what else?"

"What else? Oh! As long as the buffer hasn't been written to the Writer, the data can be deleted and/or replaced."

"Amigo! I'm impressed! You're an expert! Well, then I'll tell you about some new classes: ByteArrayStream and PrintStream."

"For example, ByteArrayInputStream and ByteArrayOutputStream."

"These classes are a bit like StringReader and StringWriter. Except StringReader reads characters (char) from a string (String), but InputStream reads bytes from a byte array (ByteArray)."

StringWriter writes characters (char) to a string, while ByteArrayOutputStream writes bytes to an internal byte array. When you write to a StringWriter, its internal string grows longer, and when you write to a ByteArrayOutputStream its internal byte array also expands dynamically.

"Remember the example you were given in the last lesson:"

Reading from the reader object and writing to the writer object:
public static void main (String[] args) throws Exception
{
 String test = "Hi!\n My name is Richard\n I'm a photographer\n";
 StringReader reader = new StringReader(test);

 StringWriter writer = new StringWriter();

 executor(reader, writer);

 String result = writer.toString();

 System.out.println("Result: "+ result);
}

public static void executor(Reader reader, Writer writer) throws Exception
{
 BufferedReader br = new BufferedReader(reader); String line; while ((line = br.readLine()) != null) { StringBuilder sb = new StringBuilder(line); String newLine = sb.reverse().toString();

 writer.write(newLine);
 }
}

"Here is how it would look if it worked using bytes instead of characters:"

Reading from an InputStream object and writing to an OutputStream object:
public static void main (String[] args) throws Exception
{
 String test = "Hi!\n My name is Richard\n I'm a photographer\n";
 InputStream inputStream = new ByteArrayInputStream(test.getBytes());

 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

 executor(inputStream, outputStream);

 String result = new String(outputStream.toByteArray());
 System.out.println("Result: "+ result);
}

public static void executor(InputStream inputStream, OutputStream outputStream) throws Exception
{
 BufferedInputStream bis = new BufferedInputStream(inputStream);
 while (bis.available() > 0)
 {
  int data = bis.read();
  outputStream.write(data);
 }
}

"Everything is the same as in the example above. Except, we replaced String with ByteArray, Reader with InputStream, and Writer with OutputStream."

"The only two other steps are converting the String into a ByteArray and back again. As you can see, this is done quite easily:"

Converting a String into a ByteArray and back again
public static void main (String[] args) throws Exception
{
 String test = "Hi!\n My name is Richard\n I'm a photographer\n";
 byte[] array = test.getBytes();

 String result = new String(array);
 System.out.println("Result: "+ result);
}

"To get the bytes that have already been added to a ByteArrayOutputStream, call the toByteArray() method."

"Ah. The similarities to StringReader/StringWriter are pretty strong, especially after you pointed them out to me. Thank you, Ellie, for a really interesting lesson."

"Where are you off to in such a hurry? I still have a small gift for you. I want to tell you about the PrintStream class."

"PrintStream? That's the first time I've heard of that class."

"Yep. Especially, if you don't count the fact that you've been using it since day one of your Java studies. Do you remember System.out? Well, System.out is a static (class) variable of the System class, and its type is... PrintStream! This is where all of these print, println, etc. methods stem from."

"Wow. How interesting. I somehow never even considered that. Tell me more."

"Good. Okay, listen up. The PrintStream class was invented for readable output. It consists almost entirely of print and println methods. Look at this table:"

Methods Methods
void print(boolean b) void println(boolean b)
void print(char c) void println(char c)
void print(int c) void println(int c)
void print(long c) void println(long c)
void print(float c) void println(float c)
void print(double c) void println(double c)
void print(char[] c) void println(char[] c)
void print(String c) void println(String c)
void print(Object obj) void println(Object obj)
void println()
PrintStream format (String format, Object ... args)
PrintStream format (Locale l, String format, Object ... args)

"There are also several format methods so you can output data using a format string. For example:"

Converting a String into a ByteArray and back again
String name = "Kolan";
int age = 25;
System.out.format("My name is %s. My age is %d.", name, age);
Screen output:
My name is Kolan. My age is 25.

"Yeah, I remember. We already studied the String class's format method."

"That's all for now."

"Thanks, Ellie."

Comments (7)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Thomas Level 41, Bayreuth, Germany
25 March 2023
"What else? Oh! As long as the buffer hasn't been written to the Writer, the data can be deleted and/or replaced." Please explain that in detail using the possibilities of the BufferedWriter class (or is it more like extending BufferedWriter)
matemate123 Level 50, Kraków, Poland
30 August 2023
This definietly curious. Do you have any details about it now? I found something like that: "Imagine you're writing several lines of text to a BufferedWriter. You've added these lines to the buffer, but they haven't been written out to the actual file yet. During this time, you can modify or delete the content you've written in the buffer before it gets permanently written to the file. For instance, if you realize you made a mistake or want to change something in what you've written so far, you can edit the buffer's content before calling the flush() method to write the data to the file. Once you call flush(), the data in the buffer becomes official and is written to the file as-is." Maybe it something like that:

try {
            FileWriter fileWriter = new FileWriter("output.txt");
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // Writing data to the buffer
            bufferedWriter.write("Hello, ");
            bufferedWriter.write("BufferedWriter!");

            // Buffer hasn't been written to the Writer yet? then
            // Modifying the data in the buffer before flushing
            bufferedWriter.write(" Let's learn more.");

            // Now, we'll flush the buffer to the writer
            bufferedWriter.flush();

            // Close the BufferedWriter
            bufferedWriter.close();

            System.out.println("Data written to file.");
        } catch (IOException e) {
            e.printStackTrace();
        }
Thomas Level 41, Bayreuth, Germany
30 August 2023
You could just look at the Java API Docs. There is no way to access the buffer. What you can do: 1. try to use reflection to make the buffer accessible. But afair these possibilities have been reduced lately, see here AccessibleObject.setAccessible 2. Extend BufferedWriter or write your own version of it with access to the buffer. I just wrote a class decorating OutputStream and named it BufferedOutputStream. But I still do not know what to do with the buffer. You do not know when the buffering class will flush and in what state the buffer is?
Thomas Level 41, Bayreuth, Germany
30 August 2023

import java.io.IOException;
import java.io.OutputStream;

public class BufferedOutputStream extends OutputStream {

    protected OutputStream out;
    protected byte[] buffer;
    protected int pointer;


    public BufferedOutputStream(int bufferSize, OutputStream out) {
        this.out = out;
        buffer = new byte[bufferSize];
    }

    @Override
    public void close() throws IOException {
        flush(); // flush before close
        out.close(); // delegate
    }

    @Override
    public void flush() throws IOException {
        if (pointer > 0) {
            out.write(buffer, 0, pointer);
            pointer = 0;
        }
        out.flush(); // delegate
    }

    @Override
    public void write(int b) throws IOException {
        buffer[pointer] = (byte) b;
        pointer++;
        flushIfBufferFull();
    }

    @Override
    public void write(byte[] b) throws IOException {
        for (int i = 0; i < b.length ; i++) {
            buffer[pointer] = b[i];
            pointer++;
            flushIfBufferFull();
        }
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        for (int i = 0; i < off + len ; i++) {
            buffer[pointer] = b[i];
            pointer++;
            flushIfBufferFull();
        }
    }

    private void flushIfBufferFull() throws IOException {
        if (pointer == buffer.length) {
            out.write(buffer); // delegate write
            pointer = 0;
        }
    }
    
    public byte[] getBuffer() {
        return buffer;
    }

}
Thomas Level 41, Bayreuth, Germany
30 August 2023
Test class. It sleeps some seconds after writing and in that time the target file should not have the content written. When leaving the try block (implicitly calling auto close) the data is written. WIthin that time you could call the getBuffer method to output the buffer to default out.

import java.io.FileOutputStream;
import java.io.IOException;

public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        try (BufferedOutputStream out =
                     new BufferedOutputStream(1024, new FileOutputStream("c:\\Temp\\out.txt"))) {

            out.write("""
                    Test data written to hd.
                    Grabbing the buffer and sending it to default out.
                    Displaying the entire buffer (1024 bytes) and not just the written data.
                    For that you need to publish the pointer, too,
                    """.getBytes());

            System.out.println(new String(out.getBuffer()));
            Thread.sleep(4000);
        }
    }
}
Nicholas Grube Level 41, Carrollton, TX, United States
4 March 2020
"You have to pass to the BufferedReader constructor the Reader object that data will be read from. A BufferedReader object reads big chunks of data from the Reader and stores them internally in a buffer. That's why using a BufferedReader to read from a Reader is faster than reading directly from a Reader." should be in blue text.
Austeja Level 10, Kaunas, Lithuania
28 December 2019
Why can't I simply check out the previous classes? ;/