Now first of all, I would not be asking this is I knew nothing about input and output. I've watched tons of tutorials on PrintWriter, FileInputStream, InputStreamReader and IOException. I understand the concept of these classes. But I just don't understand this code. I tried running it. First, there was an output saying "two lines: ". I typed "hello" in the first line and "I am Bob" in the 2nd line. When I pressed enter, the program just output exactly what I wrote. Here's a sample of what came up on the console:
two lines:
hello
I am Bob
hello
I am Bob
The program continues running after this. Can someone please explain the code to me? I'm seeing some new things in this code like System.getProperty ("line.separator"). What is the purpose of this method? What do the while-loops and for-loops do? Why is fout.flush() commented out? What happens if I include that into the code? I am so confused. Here's the code:
import java.io.*;
// File, PrintWriter, FileInputStream, InputStreamReader, IOException
class Demo
{
public static void main (String[] args)
{
PrintWriter out = new PrintWriter (System.out, true);
InputStreamReader in = new InputStreamReader (System.in);
File file = new File ("file.txt");
try (
PrintWriter fout = new PrintWriter (file);
InputStreamReader fin = new InputStreamReader (new FileInputStream (file)))
{
out.println ("two lines:");
String line1 = readLine (in);
String line2 = readLine (in);
out.println (line1);
out.println (line2);
out.println ();
fout.println (line1);
fout.println (line2);
//fout.flush ();
line1 = readLine (fin);
line2 = readLine (fin);
out.println (line1);
out.println (line2);
}
catch (IOException e)
{
e.printStackTrace ();
}
}
public static String readLine (InputStreamReader in) throws IOException
{
String sepChars = System.getProperty ("line.separator");
int countChars = sepChars.length ();
StringBuilder sb = new StringBuilder ();
char c = (char) in.read ();
while (!sepChars.contains ("" + c))
{
sb.append (c);
c = (char) in.read ();
}
for (int i = 0; i < countChars - 1; i++)
c = (char) in.read ();
String rad = sb.toString ();
return rad;
}
}
You probably know that System.getProperty ("line.separator")
returns string representing the native newline character sequence (e.g. \n for Linux, \r\n for Windows, etc). The readLine
method, as its name suggests, reads entire line from a given InputStreamReader
until that newline has been read, and discards any extra newline characters following the line without validating if it is a correct newline at all (if any; I mean if the line separator is longer than a byte). For instance, If the newline string was \r\n
, it would eventually encounter \r
and stop the first loop, and read one more character which is assumed to be \n
.
In the main
method, it would create two InputStreamReader
respectively called in
(which reads from stdin) and fin
(which reads from file.txt), and two PrintWriter
respectively called out
(which prints to stdout) and fout
(which writes to file.txt).
It would then
in
)out
)fout
)fin
)out
)One might expect that the step 4 would read the same two lines which was written in step 3.
However, the fout.flush()
is commented out, so the step 3 would just keep the two lines in the buffer (and not actually write (or flush) them to file!) and cause step 4 to hang forever (If the file.txt is empty, the in.read()
would return -1 (End Of File, EOF) which is converted to U+FFFF by (char) in.read()
, which is not a \n
so again reads and gets -1, reads again and gets -1 again, and so on. Tested on my Linux machine)