Java Review:
Copy Programs

A standard simple program in a Unix environment is to copy the standard input to the standard output. Here is a standard simple version in the C language:

/* copy.c: simplest C copy program
 *    copies stdin to stdout
 */
#include <stdio.h>
int main(void) {
   char ch;
   while ((ch = getchar()) != EOF)
      putchar(ch);
}

The previous copy program also compiles and runs under C++, but C++ provides a different style. Note that the C++ function cin.get(ch); uses a reference parameter, not available in C or in Java, so that after the call, the value of the next character "pops into" the variable ch. (In C, one would have to pass the address of ch, that is &ch, and in Java this style is not possible.)

// copy.cc: simplest C++ copy program
//    copies cin to cout
#include <iostream.h>
int main() {
   char ch;
   while (cin.get(ch))
      cout.put(ch);
}

The shortest corresponding program in Java is a little more complex. Sending a character to the standard output is simple and similar: System.out.print(ch);, but input requires that one takes an I/O exception condition into account. Also, the function read() returns an int. Without the cast (char)ch below, the program would print the Ascii integer value, rather than the character. (This and other programs here were adapted from the Gosling Java text.)


// CopyFirst.java: copy standard input to standard output 
import java.io.*;
class CopyFirst {
   public static void main(String[] args) throws IOException {
      int ch;
      while ((ch = System.in.read()) != -1)
         System.out.print((char)ch);
   }
}

Instead of directly invoking System.in.read, one can open the standard input, as in the program below:


// Copyss.java: copy standard input to standard output (SuperSimple version)
import java.io.*;
class Copyss {
   public static void main(String[] args) throws IOException {
      int ch;
      Reader in = new InputStreamReader(System.in);
      while ((ch = in.read()) != -1)
         System.out.print((char)ch);
   }
}

Here is the same program written using a separate method to read a character. In this case the function getNextChar() is returning a char, so when it gets to end-of-file and fetches the int with value -1, this is converted to the character code 65535. Thus the while needs to be as it is below, or needs to be while ((ch = copy.getNextChar()) != 65535).

// Copys.java: copy standard input to standard output (Simple version)
import java.io.*;
class Copys {
   // internal file name for input stream
   private Reader in = new InputStreamReader(System.in); 

   // getNextChar: fetches next char.
   private char getNextChar() throws IOException {
      return (char)in.read();
   }

   // main: for copy program
   public static void main(String[] args) throws IOException {
      Copys copy = new Copys();
      char ch;
      while ((byte)(ch = copy.getNextChar()) != -1)
         System.out.print(ch);
   }
}

Here is another similar Java program, but it catches any I/O exceptions right at the read, so we don't have to worry about the higher level functions throwing an I/O exception. This version also has an explicit constructor.

// Copy0.java: copy standard input to standard output
//    Usage:  % java Copy0
import java.io.*;
public class Copy0 {
   
   // internal file name for input stream
   private Reader in;

   // Copy0: constructor
   public Copy0() {
      in = new InputStreamReader(System.in); 
   }

   // getNextChar: fetches next char.
   public char getNextChar() {
      char ch = ' '; // = ' ' to keep compiler happy
      try {
         ch = (char)in.read();
      } catch (IOException e) {
         System.out.println("Exception reading character");
      }
      return ch;
   }

   // main: for copy program
   public static void main(String[] args) {
      Copy0 copy = new Copy0();
      char ch;
      while ((byte)(ch = copy.getNextChar()) != -1)
         System.out.print(ch);
   }
}
% java Copy0
Now is the time
Now is the time
kdflaksdgag
kdflaksdgag             type return followed by ctrl-D
% java Copy0 < Test.text
Now is the time
for all good men
to come to the aid of their party.

Here is a version of the copy program that copes either with the standard input or with a named input file. This program also has moved the actual copying into the class as a method.

// Copy.java: copy an input file or the standard input to the standard output
//    Usage:  % java Copy [infile]
import java.io.*;
class Copy {
   private Reader in; // internal file name for input stream

   // Copy: constructor, input parameter: input file name or null
   public Copy(String fileName) {
      if (fileName == null)
         in = new InputStreamReader(System.in);
      else {
         try {
             in = new FileReader(fileName);
         } catch (IOException e) {
            System.out.println("Exception opening " + fileName);
         }
      }
   } 

   // getNextChar: fetches next char.  Also opens input file
   private char getNextChar() {
      char ch = ' '; // = ' ' to keep compiler happy
      try {
         ch = (char)in.read();
      } catch (IOException e) {
         System.out.println("Exception reading character");
      }
      return ch;
   }

   // copyFile: actually copy the input to the output
   public void copyFile() {
      char ch;
      while ((byte)(ch = getNextChar()) != -1)
         System.out.print(ch);
   }

   // main: 
   public static void main(String[] args) {
      Copy copy;
      // pass an input file name if present on command line
      if (args.length > 0)
         copy = new Copy(args[0]);
      else
         copy = new Copy(null);
      copy.copyFile();
   }
}
% java Copy 
ksdljgsa
ksdljgsa
lkjfdslkjflkds
lkjfdslkjflkds         type return followed by ctrl-D
% java Copy Test.text
Now is the time
for all good men
to come to the aid of their party.
% java Copy < Test.text
Now is the time
for all good men
to come to the aid of their party.

Here is a more complex program yet that has either the standard input or a named input file, along with the standard output or a named output file:

// Copy2.java: copy an input file or the standard input
//    to an output file or to the standard output
//    Usage:  % java Copy2  [infile  [outfile] ]
import java.io.*;
class Copy2 {
   
   private Reader in; // internal file name for input stream
   private Writer out; // internal file name for output stream

   // Copy2: constructor, input parameters: input and output file names
   public Copy2(String inFileName, String outFileName) {
      if (inFileName == null)
         in = new InputStreamReader(System.in);
      else {
         try {
            in = new FileReader(inFileName);
         } catch (IOException e) {
            System.out.println("Exception opening " + inFileName);
         }
      }
      if (outFileName == null)
         out = new OutputStreamWriter(System.out);
      else {
         try {
            out = new FileWriter(outFileName);
         } catch (IOException e) {
            System.out.println("Exception opening " + outFileName);
         }
      }
   } 

   // getNextChar: fetches next char.  Also opens input file
   private char getNextChar() {
      char ch = ' '; // = ' ' to keep compiler happy
      try {
         ch = (char)in.read();
      } catch (IOException e) {
         System.out.println("Exception reading character");
      }
      return ch;
   }
   
   // putNextChar: output the next character
   private void putNextChar(char ch) {
      try {
         out.write((int)ch);
      } catch (IOException e) {
         System.out.println("Exception writing character");
      }
   }

   // copyFlush: flush the output (i.e., finish writing to disk)
   public void copyFlush() {
      try {
         out.flush();
      } catch (IOException e) {
         System.out.println("Exception flushing output");
      }
   }

   // copyClose: close the output file
   public void copyClose() {
      try {
         out.close();
      } catch (IOException e) {
         System.out.println("Exception closing output");
      }
   }

   // copyFile: actually copy the input to the output
   public void copyFile() {
      char ch;
      while ((byte)(ch = getNextChar()) != -1)
         putNextChar(ch);
      copyFlush();
      copyClose();
   }

   // main: 
   public static void main(String[] args) {
      Copy2 copy;
      // pass an input file name if present on command line
      if (args.length == 1)
         copy = new Copy2(args[0], null);
      else if (args.length == 2)
         copy = new Copy2(args[0], args[1]);
      else 
         copy = new Copy2(null, null);
      copy.copyFile();
   }
}
% java Copy2
jdkdgjg
kjfhkjfkjhfkj  type ctrl-D to simulate EOF
jdkdgjg
kjfhkjfkjhfkj
% java Copy2 < Testin.txt
Now is the time
for all good men
to come to the aid of their party.
% java Copy2 Testin.txt
Now is the time
for all good men
to come to the aid of their party.
% java Copy2 Testin.txt Testout.txt
% cat Testout.txt
Now is the time
for all good men
to come to the aid of their party.

Finally, here is a simpler version that only has a named input file and a named output file:

// Copy3.java: copy an input file (not the standard input)
//    to an output file (not the standard output)
//    Usage:  % java Copy3  infile  outfile
import java.io.*;
class Copy3 {
   
   private Reader in; // internal file name for input stream
   private Writer out; // internal file name for output stream

   // Copy3: constructor, input parameters: input and output file names
   public Copy3(String inFileName, String outFileName) {
      try {
         in = new FileReader(inFileName);
      } catch (IOException e) {
         System.out.println("Exception opening " + inFileName);
      }
      try {
         out = new FileWriter(outFileName);
      } catch (IOException e) {
         System.out.println("Exception opening " + outFileName);
      }
   } 

   // getNextChar: fetches next char.  Also opens input file
   private char getNextChar() {
      char ch = ' '; // = ' ' to keep compiler happy
      try {
         ch = (char)in.read();
      } catch (IOException e) {
         System.out.println("Exception reading character");
      }
      return ch;
   }
   
   // putNextChar: output the next character
   private void putNextChar(char ch) {
      try {
         out.write((int)ch);
      } catch (IOException e) {
         System.out.println("Exception writing character");
      }
   }

   // copyFlush: flush the output (i.e., finish writing to disk)
   public void copyFlush() {
      try {
         out.flush();
      } catch (IOException e) {
         System.out.println("Exception flushing output");
      }
   }

   // copyClose: close the output file
   public void copyClose() {
      try {
         out.close();
      } catch (IOException e) {
         System.out.println("Exception closing output");
      }
   }

   // copyFile: actually copy the input to the output
   public void copyFile() {
      char ch;
      while ((byte)(ch = getNextChar()) != -1)
         putNextChar(ch);
      copyFlush();
      copyClose();
   }

   // main: 
   public static void main(String[] args) {
      Copy3 copy = null; // = null to keep compiler happy
      // pass an input file name and output file name on command line
      if (args.length == 2)
         copy = new Copy3(args[0], args[1]);
      else {
         System.out.println("Usage: % java Copy3 infile outfile");
         System.exit(1);
      }
      copy.copyFile();
   }
}
% javac Copy3.java
% java Copy3
Usage: % java Copy3 infile outfile
% java Copy3 Testout.txt
Usage: % java Copy3 infile outfile
% java Copy3 Testin.txt Testout.txt
% cat Testout.text
Now is the time
for all good men
to come to the aid of their party.