Using a PushbackReader

The class PushbackReader is useful if you want to look ahead at the incoming stream of data. It allows you to unread the data that you have read. The amount of data that can be unread can be specified.

Here’s a simple Parser example that puts it into practice. The main method creates an instance and passes as an argument a reader that points to the Character stream to be processed, in this case a FileReader. Then it repeatedly reads tokens until EOF. The Parser’s getNextToken method reads in one character to determine what method it should call and unreads that character back onto the stream.

Main.java:

import java.util.*;
import java.io.*;
  
public class Main {   
   public static void main(String[] args) throws Exception {
      if (args.length != 1) { 
         System.out.println("Usage: java Main <file>");
         System.exit(1);
      }
 
      BufferedReader br = new BufferedReader(new FileReader(args[0]));
      Parser parser = new Parser(br);
      String token;
      while ((token = parser.getNextToken()) != null) {
         System.out.println("Found: " + parser.getType() + "t[" + token + "]");
      }
   }
}
 
class Parser
{
   private final static int NOTOKEN  = 0;
   private final static int VARIABLE = 1;
   private final static int ASSIGN   = 2;
   private final static int SEMICOL  = 3;
 
   PushbackReader pr;
   String token = null;
   int type = NOTOKEN;
 
   public Parser(Reader r) {
      pr = new PushbackReader(r);
   }
 
   public String getType() {
      if (type == VARIABLE) return "VARIABLE";
      else if (type == ASSIGN) return "ASSIGN";
      else if (type == SEMICOL) return "SEMICOL";
  
      return "NOTOKEN";
   }
   
   public String getNextToken() throws IOException {
      token = null;
      type = NOTOKEN;
 
      skipWhitespace();
      int k = pr.read();
      if (k != -1) {
         char c = (char) k;
         pr.unread(k);
         if (Character.isLetter(c)) {
            parseVariable();
         }
         else if (c == ':') {
            parseAssign();
         }
         else if (c == ';') {
            parseSemicol();
         }
         else {
            parseError("Unrecognized character: " + c);
         }
      }
  
      return token;
   }
 
   public void skipWhitespace() throws IOException {
      int k;
      while ((k = pr.read()) > -1) {
         if (!Character.isWhitespace((char) k)) {
            pr.unread(k);
            break;
         }
      }   
   }
  
   private void parseVariable() throws IOException {
      StringBuffer buffer = new StringBuffer();
      int k;
      type = VARIABLE;
      while ((k = pr.read()) > -1) {
         if (Character.isLetter((char) k) || Character.isDigit((char) k)) {
            buffer.append((char) k);
         }
         else {
            break;
         }
      }
 
      token = buffer.toString();
   }
 
   private void parseAssign() throws IOException {
      pr.read();   // read ':'  which we already know
      int k = pr.read();
      if (k > -1 && ((char) k) == '=') {
         type = ASSIGN;
         token = ":=";
      }
      else {
         parseError("Expected character '=', found character '" + (char) k + "'");
      }
   }
 
   private void parseSemicol() throws IOException {
      pr.read();   // read ';'  which we already know
      type = SEMICOL;
      token = ";";
   }
 
   private void parseError(String error) {
      System.err.println("ParseError: " + error);
   }
}

Using it on the file test.pas:

a := b;
var1 := var2;

results in:

Found: VARIABLE	[a]
Found: ASSIGN	[:=]
Found: VARIABLE	[b]
Found: SEMICOL	[;]
Found: VARIABLE	[var1]
Found: ASSIGN	[:=]
Found: VARIABLE	[var2]
Found: SEMICOL	[;]

Using it on the file test2.pas:

a12345 := b0003
var1 = var2;

outputs:

Found: VARIABLE [a12345]
Found: ASSIGN   [:=]
Found: VARIABLE [b0003]
Found: VARIABLE [var1]
ParseError: Unrecognized character: =

Keeping track of line numbers when reading a file with a Reader

Use the class filtered reader LineNumberReader. While getting the actual read buffer from the reader, you can also retrieve the current line number. You can also (re)set it with setLineNumber. This does not affect the file pointer.

This example reads in a textfile and displays a separator line every 10 lines.

Main.java:

import java.io.*;
  
public class Main {
   public static void main(String args[]) {
      if (args.length != 1) {
         System.out.println("Usage: java Main <textfile>");
         System.exit(1);
      }
  
      try {
         LineNumberReader lnr = new LineNumberReader(new FileReader(args[0]));
         String line;
         while ((line = lnr.readLine()) != null) {
            if ((lnr.getLineNumber() % 10) == 0)
               System.out.println("------------------------------------------");
            System.out.println(line);
         }
      }
      catch(IOException e) {
         System.out.println(e);
      }
   }
}

Converting a character array to a Reader

Use the class CharArrayReader. Once you have wrapped it into a reader, you can use other readers to access the data, as shown in following example.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String args[]) {
      char array[] = { 'h', 'e', 'l', 'l', 'o', ',', 'n', 
                       'w', 'o', 'r', 'l', 'd', '!' };
      CharArrayReader car = new CharArrayReader(array);
 
      BufferedReader br = new BufferedReader(car);
      String line;
      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }
   }
}

outputs:

hello,
world!

Writing a file in UTF-8 encoding using a Writer

You can specify the encoding UTF-8 with OutputStreamWriter. This class will convert the outgoing Unicode to UTF-8 bytes.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String args[]) {
      try {
         OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test.utf"), "UTF8");
         BufferedWriter bw = new BufferedWriter(osw);
         bw.write("hu0000ello");
         bw.close();
      }
      catch(FileNotFoundException e) {
         System.out.println(e);
      }
      catch(IOException e) {
         System.out.println(e);
      }
   }
}

Reading a file that is encoded in UTF-8 using a Reader

You can specify the encoding UTF-8 with InputStreamReader. This class will convert the incoming bytes to Unicode.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String args[]) {
      if (args.length != 1) {
         System.out.println("Usage: java Main <utf8file>");
         System.exit(1);
      }
    
      try {
         InputStreamReader isr = new InputStreamReader(new FileInputStream(args[0]), "UTF8");
         BufferedReader br = new BufferedReader(isr);
         String line;
         while ((line = br.readLine()) != null) {
            System.out.println(line);
         }
         br.close();
      }
      catch(FileNotFoundException e) {
         System.out.println(e);
      }
      catch(IOException e) {
         System.out.println(e);
      }
   }
}

Note: Since UTF-8 is backwards compatible with ASCII, you can test this program with those types of files.

Writing a text file using FileWriter

Main.java:

import java.io.*;
 
public class Main
{
   public static void main(String []args) {
      String lineSeparator = System.getProperty("line.separator");
      StringBuffer sb = new StringBuffer();
      sb.append("Hello");
      sb.append(lineSeparator);
      sb.append("World!");
      sb.append(lineSeparator);
 
      try {
         BufferedWriter bw = new BufferedWriter(new FileWriter("textfile.txt"));
         bw.write(sb.toString());
         bw.close();
      } 
      catch(IOException e) {
         e.printStackTrace();
      }
   }
} 

Teading a text file using FileReader

Main.java:

import java.io.*;
 
public class Main
{
   public static void main(String []args) {
      if (args.length != 1) {
         System.out.println("Usage: java Main <textfile>");
         System.exit(1);
      }
   
      try {
         BufferedReader br = new BufferedReader(new FileReader(args[0]));
         String line;
         StringBuffer total = new StringBuffer();
         while ((line = br.readLine()) != null) {
            total.append(line);
            total.append(System.getProperty("line.separator"));
         }
         System.out.println(total);
 
         br.close();
      }
      catch(FileNotFoundException e) {
         e.printStackTrace();
      }
      catch(IOException e) {
         e.printStackTrace();
      }
   }
}

Serializing an array in Java

Main.java:

import java.io.*;
 
public class Main
{
   public static void main(String []args) throws Exception {
      String stringArray[] = { "this", "is", "a", "test" };
 
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.ser"));
      oos.writeObject(stringArray);
      oos.close();
 
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser"));
      String stringArray2[] = (String[]) ois.readObject();           
      ois.close();
   
      for (int i=0; i<stringArray2.length; i++) {
         System.out.println(stringArray2[i]);
      }
   }
}

Get the SUID of a class at runtime

Invoke the method getSerialVersionUID on an instance of the class ObjectStreamClass.

Main.java:

import java.io.*;
 
public class Main implements Serializable {
   public static void main(String args[]) {
      ObjectStreamClass osc = ObjectStreamClass.lookup(Test1.class);
      System.out.println("serialVersionUID for class Test1: " + osc.getSerialVersionUID());
 
      osc = ObjectStreamClass.lookup(Test2.class);
      System.out.println("serialVersionUID for class Test2: " + osc.getSerialVersionUID());
   }
}
 
class Test1 implements Serializable {
}
 
class Test2 implements Serializable {
   static final long serialVersionUID = 1;
}

outputs:

serialVersionUID for class Test1: 3264391750135642662
serialVersionUID for class Test1: 1

Why static fields are not serialized

Static fields of a class are shared among objects of that class. Just imaging they would be serialized, what would happen if you have instantiated an object of a class containing an important static variable and that you would deserialize an object of the same class. The static field would be overwritten. Sometimes, however, you may want this type of behavior. The solution is simple: just customize serialization through the methods writeObject and readObject as shown in following example.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String args[]) {
      try {
         Test t1 = new Test();
         t1.create();
 
         System.out.println("State of t1: " + t1);
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(t1);
         oos.flush();
 
         // make static variable count 0 to prove our point
         Test.count = 0;
 
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
         ObjectInputStream ois = new ObjectInputStream(bais);
         Test t2 = (Test) ois.readObject();
 
         System.out.println("State of t2: " + t2);  
      }
      catch(ClassNotFoundException e) {
         System.out.println(e); 
      }
      catch(IOException e) { 
         System.out.println(e); 
      }
   }
}
 
class Test implements Serializable {
   public int var = 0;
   public static int count = 0;
  
   Test() { }
 
   public void create() {
      var = 1;
      count++;
   }
 
   private void writeObject(ObjectOutputStream oos) throws IOException {
      oos.defaultWriteObject();
      // write static variable
      oos.writeInt(count);
   }
 
   private void readObject(ObjectInputStream ois) throws IOException, 
                                              ClassNotFoundException {
      ois.defaultReadObject();
      // read static variable
      count = ois.readInt();
   }
  
   public String toString() {
      return "var = " + var + "; count = " + count;
   }
}

outputs:

State of t1: var = 1; count = 1
State of t2: var = 1; count = 1