User Professor Hans Noodles
Professor Hans Noodles
Level 41

Practice working with the BuffreredReader and InputStreamReader classes

Published in the Java Developer group
Hi! Today's lesson will be divided into two parts for convenience. We'll repeat some old topics that we touched on previously, and we'll consider some new features :) Practice working with the BuffreredReader and InputStreamReader classes - 1 Let's start with the first one. You've already a class like BufferedReader many times. I hope you haven't had time to forget this statement:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Before reading further, try to remember what each component — System.in, InputStreamReader, BufferedReader — is responsible for and why it is needed. Did you remember? If not, no worries. :) If you've forgotten something, reread this lesson, which is dedicated to reader classes. We'll briefly recall what each of them can do. System.in — this is a stream for receiving data from the keyboard. In principle, it alone would be enough to implement the logic required to read text. But, as you will recall, System.in can only read bytes, not characters:

public class Main {

   public static void main(String[] args) throws IOException { 

       while (true) { 
           int x = System.in.read(); 
           System.out.println(x); 
       } 
   } 
} 
If we execute this code and enter the Cyrillic letter "Й", the output will be:

Й
208
153
10 
Cyrillic characters occupy 2 bytes in memory, and they are displayed on the screen. The number 10 is the decimal representation of a line feed character, i.e. from pressing Enter. Reading bytes is such a pleasure, so using System.in isn't very convenient. In order to correctly read Cyrillic (and other) letters, we use InputStreamReader as a wrapper:

public class Main { 

   public static void main(String[] args) throws IOException { 

       InputStreamReader reader = new InputStreamReader(System.in); 
       while (true) { 
           int x = reader.read(); 
           System.out.println(x); 
       } 
   } 
} 
We enter the same letter "Й", but the result is different this time:

Й 
1049 
10
InputStreamReader converted two bytes (208 and 153) to the single number 1049. This is what it means to read characters. 1049 corresponds to the Cyrillic letter "Й". We can easily convince ourselves that this is true:

public class Main { 

   public static void main(String[] args) throws IOException { 
       char x = 1049; 
       System.out.println(x); 
   } 
} 
Console output:

Й
And as forBufferedReader (and in general, BufferedAnythingYouWant), buffered classes are used to optimize performance. Accessing a data source (file, console, web resource) is quite expensive in terms of performance. Therefore, in order to reduce the number of accesses, BufferedReader reads and accumulates data in a special buffer, and we get it from there. As a result, the number of times the data source is accessed is slashed — possibly by several orders of magnitude! Another one of BufferedReader's features and its advantage over the ordinary InputStreamReader, is the extremely helpful readLine() method, which reads entire lines of data, not individual numbers. This, of course, is super convenient when dealing with large texts. Here's what reading lines looks like:

public class Main { 

   public static void main(String[] args) throws IOException { 

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 
       String s = reader.readLine(); 
       System.out.println ("The user entered the following text:"); 
       System.out.println(s); 
       reader.close(); 
   } 
}

BufferedReader+InputStreamReader is faster than InputStreamReader alone 
The user entered the following text: 
BufferedReader+InputStreamReader is faster than InputStreamReader alone
Practice working with the BuffreredReader and InputStreamReader classes - 2Of course, BufferedReader is very flexible. You're not limited to working with the keyboard. For example, you can read data directly from the web, simply by passing the required URL to a reader:

public class URLReader { 

   public static void main(String[] args) throws Exception { 

       URL oracle = new URL("https://www.oracle.com/index.html"); 
       BufferedReader in = new BufferedReader( 
               new InputStreamReader(oracle.openStream())); 

       String inputLine; 
       while ((inputLine = in.readLine()) != null) 
           System.out.println(inputLine); 
       in.close(); 
   } 
}
You can read data from a file by passing the file path:

public class Main { 

   public static void main(String[] args) throws Exception { 

       FileInputStream fileInputStream = new FileInputStream("testFile.txt"); 
       BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); 

       String str; 

       while ((str = reader.readLine()) != null)   { 
           System.out.println (str); 
       } 

       reader.close(); 
   } 
}

Replacing System.out

Now let's take a look at an interesting capability that we haven't touched on before. As you surely remember, the System class has two static fields — System.in and System.out. These twin brothers are stream objects. System.in is an InputStream. And System.out is a PrintStream. Right now, we'll talk about System.out. If we drop into the System class's source code, we see this:

public final class System { 
……………... 
public final static PrintStream out = null; 
 ………… 
} 
Thus, System.out  is simply an ordinary static variable of the System class. There's nothing magic about it :) The out variable is a PrintStream reference. Here's an interesting question: When System.out.println() is executed, why exactly does the output go to the console and not somewhere else? And can this be changed somehow? For example, suppose we want to read data from the console and write it to a text file. Is it possible to somehow implement this simply using System.out rather than additional reader and writer classes? Indeed, it is :) And we can do it even though the System.out variable is marked with the final modifier!  Practice working with the BuffreredReader and InputStreamReader classes - 3So what do we need to make this happen?  First of all, we need a new PrintStream object to replace the current one. The current object, set in the System class by default, doesn't serve our purposes: it points to the console. You need to create a new one that points to a text file — the "destination" for our data. Second, we need to understand how to assign a new value to the System.out variable. You can't use a simple assignment operator, because the variable is marked final. Let's work backwards from the end. As it happens, the System class has the method we need: setOut(). It takes a PrintStream object and sets it as the destination for output. That's just what we need! All that remains is to create a PrintStream object. This is also easy:

PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
The full code will look like this:

public class SystemRedirectService { 

   public static void main(String arr[]) throws FileNotFoundException 
   { 
       PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
       /* Save the current value of System.out in a separate variable so that later 
       we can switch back to console output */ 

       PrintStream console = System.out; 
       // Assign a new value to System.out 
       System.setOut(filePrintStream); 
       System.out.println("This line will be written to the text file"); 

       // Restore the old value of System.out 
       System.setOut(console); 
       System.out.println("But this line will be output to the console!"); 
   } 
}
As a result, the first string is written to the text file, and the second is displayed in the console :) You can copy this code into your IDE and run it. Open the text file and you'll see that the string has been successfully written there :) With this, our lesson has come to the end. Today we recalled how to work with streams and readers. We recollected how they differ from one another and learned about some new capabilities of System.out, which we've used in almost every lesson :) Until the next lessons!
Comments (7)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Ian De Bie Level 26, Los Angeles, United States
21 October 2020
how did the 2 bytes in this Cyrillic letter produce 1049? Й 208 = 11010000 153 = 10011001 1049 = 10000011001
BlueJavaBanana Level 37
7 June 2020
I'm confused as to why the final moifier does not restrict the System methods from changing the System.out field? Could someone enlighten me please?
DeltaPilot12 Level 24, Berlin, Germany
16 May 2020
If BufferedReader + InputStreamReader + FileInputStream serves the job that well, what do we need FileReader for???
fzw Level 41, West University Place, United States
13 April 2020

FileInputStream fileInputStream = new FileInputStream("testFile.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
This code look so weird. Why not simply use

BufferedReader reader = new BufferedReader(new FileReader("testFile.txt"));