Title: I/O Part2
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

I/O Part2

File Class

The File class provides many capabilities related to directories and file management. To see a complete list, go to online help. The following list provides some of the more common File characteristics. The FileManager.java program below demonstrates using some of these methods to implement a small version of the standard version of Unix commands.

One confusion is that the File class is used for files and directories. If you want to know if your class is a directory or a an actual file, call the isDirectory() method.

Constant:
File.separator - Constant string which takes on the value of "/" on Unix and "\" on Windows

- Constructors:
File(String pathname) - Creating a File class does not imply a file on the disk. The pathname string is a complete absolute pathname
- File(String dirPath, String name) - Create the full path by gluing the dirPath to the name separated by File.separator
- File (File dirParent, String name) - Some as the previous constructor, except that the directory is contained in the File dirParent class.

Methods:

- list() - Works on a File instance that happens to be a directory. returns an array of Strings containing the names of the files in the directory.
- Date lastModified() - Returns the Date of the last modification to this File
- boolean renameTo(File fnewName) - Provides a way to rename a filename
- boolean createNewFile() - Used to take a File class and make a real file out of it. If the file doesn't already exist, a file with zero bytes is created. If the file does exist, this operation will change the lastModified time on the file. This method feels a lot like the Unix touch command.
- boolean mkdir() - Create a new directory
- boolean delete() - delete a file
- boolean exists() - returns a boolean to indicate whether the file exists or not
- boolean canRead() - returns whether the File class is readable
- boolean canWrite() - returns whether the File class is writeable
- String getName() - returns the filename without the path information
- String getAbsolutePath() - returns the full pathname of the File class.

FileManager.java


import tools.*;
import java.util.*;
import java.io.*;
class FileManager
{
  String newline ;
  String userDir ;
  String extra ="";
  PrintStream out;

  FileManager(PrintStream p)
  {
    out = p;
    newline = System.getProperty("line.separator");
    userDir = System.getProperty("user.dir");
  }
  void mkdir(String parms[])
  {
    if (parms.length > 1)
    {
        String path = currPath() + File.separator + parms[1];
        File f = new File(path);
        if (!f.mkdir())
            out.println("Couldn't make directory");
    }
    else out.println("Bad mkdir --- need parameter");
  }

  void rm(String parms[])
  {
    if (parms.length > 1)
    {
        String path = currPath() + File.separator + parms[1];
        File f = new File(path);
        if (!f.delete())
            out.println("Couldn't delete file");
    }
    else out.println("Bad delete --- need parameter");
  }

  void mv(String parms[])
  {
    if (parms.length > 2)
    {
        String path = currPath() + File.separator ;
        File f = new File(path + parms[1]);
        File f2 = new File(path + parms[2]);
        if (!f.renameTo(f2))
            out.println("Couldn't rename file");
    }
    else out.println("Bad rename --- need 2 parameters");
  }


  void touch(String parms[])
  {
    if (parms.length > 1)
    {
        String path = currPath() + File.separator + parms[1];
        File f = new File(path);
        try {
            f.createNewFile();
            }
        catch (Exception e)
        {
            out.println("Exception: " + e);
        }
    }
    else out.println("Bad touch command ---- need parameter");
  }
  void ls(String parms[])
  {
    String path = currPath();
    File f = new File(path);
    File f2;
    Date date;
    String fnames[];

    fnames = f.list();
    if (fnames != null)
       for (int i=0; i < fnames.length; i++)
       {
           out.println(fnames[i]);
           if (parms.length >= 2)
           {
             f2  = new File(path, fnames[i]);
             date = new Date(f2.lastModified());
             out.println("   lastModified:   " + date);
             out.println("   isDirectory:    " + f2.isDirectory());
             out.println("   canRead:        " + f2.canRead());
             out.println("   canWrite:       " + f2.canWrite());
             out.println("   exists:         " + f2.exists());
             out.println("   getPath:        " + f2.getPath());
             out.println("   getAbsolutePath:" + f2.getAbsolutePath());
             out.println("   getParent:      " + f2.getParent());
             out.println("   getName:        " + f2.getName());
           }
       }

  }
  void pwd()
  {
    out.println("extra = " + extra);
    out.println(userDir + extra);
  }
  void cd(String parms[])
  {
    if (parms.length == 2)
    {
        String move =parms[1];
        if (move.equals(".."))
            {
                int endchar = extra.lastIndexOf(File.separator);
                if (endchar >= 0)
                        extra = extra.substring(0, endchar);
            }
         else
            {
                extra += File.separator + move;
            }
    }
    pwd();
  }
  String currPath()
  {
    return userDir + File.separator + extra;
  }


  void processCmd(String cmd_line)
  {
    String[] parms;
    int numTokens;

    StringTokenizer parse;
    parse = new StringTokenizer(cmd_line);
    numTokens = parse.countTokens();
    parms = new String[numTokens];
    for (int i=0; i < numTokens; i++)
           parms[i] = (String)parse.nextElement();

    String cmd = parms[0];


    out.println("Executing:");
    for(int i=0; i < parms.length; i++)
        out.print(parms[i] + " " );
    out.println(newline + "----");

    if (cmd.equals("pwd")) pwd();
    else if (cmd.equals("cd")) cd (parms);
    else if (cmd.equals("ls")) ls (parms);
    else if (cmd.equals("mkdir")) mkdir (parms);
    else if (cmd.equals("touch")) touch (parms);
    else if (cmd.equals("rm")) rm (parms);
    else if (cmd.equals("mv")) mv (parms);
    else out.println ("Unknown command");

    out.println("***********************");

  }

   public static void main(String[] args)
   {
     String cmd;
     boolean cont = true;
     MyInput in = new MyInput();

     FileManager fm = new FileManager(System.out);

     do
     {
        System.out.println(fm.newline +
            "Enter in new command(pwd, cd, ls, mkdir, touch, rm, mv, ls -l)"+
            fm.newline+
            "==>");
        cmd = in.get();
        if (cmd == null) break;

        fm.processCmd(cmd);
     } while (cont);
   }
}




Filtering

The following class implements directory filtering. See what happens by running:
java DirList .java

DirList.java


import java.io.*;

public class DirList implements FilenameFilter
{
  String filter = null;
  DirList(String f)
  {
    filter = f;
  }

  public boolean accept(File file, String name)
  {
    if (name.indexOf(filter) == -1)
       return false;
    else
       return true;
  }

  void ls()
  {
    File path = new File(".");
    String[] list;

    if (filter == null)
    {
        System.out.println("No Filtering List of Files");
        list = path.list();
    }
    else
    {
        System.out.println("Filter = "+ filter);
        list = path.list(this);
    }
    // Print out the list of files found in the list
    for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);

  }
  public static void main(String[] args)
  {
      DirList dir;
      if(args.length == 0)
           dir = new DirList(null);
      else
           dir = new DirList(args[0]);

      dir.ls();
  }
}



DataOutputStream, DataInputStream

If you need to write out binary datafiles, consider using the DataStream class. It's important to read in the data with the same data types that the data was written. The Data Stream classes provide methods for writing out Strings and all of the primitives (long, int, double, byte, boolean, etc. ).

Check out Online help to see all of the methods provided.

Also look at a hex dump of the file after is written with DataOutputStream. Pad provides an Hex dump format capability.

DataStreamTest.java


import java.io.*;
public class DataStreamTest {

  void writeIt(String str, String filename) throws IOException
  {
     FileOutputStream outFile = new FileOutputStream(filename);
     DataOutputStream outData = new DataOutputStream(outFile);

     outData.writeUTF(str);
     outData.close();
  }


  String readIt(String filename) throws IOException
  {
     FileInputStream inFile = new FileInputStream(filename);
     DataInputStream inData = new DataInputStream(inFile);

     String retVal = inData.readUTF();
     inData.close();
     return retVal ;
  }


  public static void main(String[] args) throws IOException
  {
        DataStreamTest dTest = new DataStreamTest();
        String filename = "fileDat.txt";

        dTest.writeIt("The universe is amazingly big.", filename);
        String str = dTest.readIt(filename);

        System.out.println("readIt read: " + str);
  }
}


DataStreamTest2.java

Use a hex editor to look at the file written with this program.


import java.io.*;
public class DataStreamTest2 {

  void writeIt(String filename) throws IOException
  {
     FileOutputStream outFile = new FileOutputStream(filename);
     DataOutputStream outData = new DataOutputStream(outFile);
     boolean b=true;
     byte b2 = 7;
     char c =  7;
     int  i =  7;
     long lng =7;
     float f = 7;
     double d =7;
     outData.writeUTF("*");     outData.writeBoolean(b);
     outData.writeUTF("*");     outData.writeByte(b2);
     outData.writeUTF("*");     outData.writeChar(c);
     outData.writeUTF("*");     outData.writeInt(i);
     outData.writeUTF("*");     outData.writeLong(lng);
     outData.writeUTF("*");     outData.writeFloat(f);
     outData.writeUTF("*");     outData.writeDouble(d);
     outData.close();
  }


  void readIt(String filename) throws IOException
  {
     FileInputStream inFile = new FileInputStream(filename);
     DataInputStream inData = new DataInputStream(inFile);

     boolean b;
     byte b2;
     char c ;
     int  i ;
     long lng ;
     float f;
     double d;
     String str;
     str = inData.readUTF();     b  =inData.readBoolean();
     str = inData.readUTF();     b2 =inData.readByte();

     // What happens if I change the next line to:
     // str = inData.readUTF();  c  =(char)inData.readByte();

     str = inData.readUTF();     c  =inData.readChar();

     str = inData.readUTF();     i  =inData.readInt();
     str = inData.readUTF();     lng=inData.readLong();
     str = inData.readUTF();     f  =inData.readFloat();
     str = inData.readUTF();     d  =inData.readDouble();

     System.out.println("b   =" + b  );
     System.out.println("b2  =" + b2 );
     System.out.println("c   =" + (int)c  );
     System.out.println("i   =" + i  );
     System.out.println("lng =" + lng);
     System.out.println("f   =" + f  );
     System.out.println("d   =" + d  );

     inData.close();
  }


  public static void main(String[] args) throws IOException
  {
        DataStreamTest2 dTest = new DataStreamTest2();
        String filename = "fileDat2.txt";

        dTest.writeIt(filename);
        dTest.readIt(filename);
  }
}


DataStreamTest3.java


import java.io.*;
public class DataStreamTest3 {

  void writeIt(String filename, byte[] barr) throws IOException
  {
     FileOutputStream outFile = new FileOutputStream(filename);
     DataOutputStream outData = new DataOutputStream(outFile);

     outData.write(barr);
     outData.close();
  }


  void readIt(String filename, byte[] barr) throws IOException
  {
     FileInputStream inFile = new FileInputStream(filename);
     DataInputStream inData = new DataInputStream(inFile);

     inData.readFully(barr);

     inData.close();
  }


  public static void main(String[] args) throws IOException
  {
        DataStreamTest3 dTest = new DataStreamTest3();
        String filename = "fileDat3.txt";
        byte[] barr = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
                       18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
        byte[] barr2 = new byte[barr.length];

        dTest.writeIt(filename, barr);
        dTest.readIt(filename, barr2);

        for (int i=0; i < barr2.length; i++)
            System.out.println("barr2["+i + "]=" + barr2[i]);
  }
}


Random.java

The RandomAccess class implements all of the methods in DataInputStream and DataOutputStream. In addition, you can open a file in a mode where it is both readable and writeable. The second parameter can take on values of "rw", "r", or "w" for Read/Write, Readable, and Writeable.

Also note the methods:

long getFilePointer() - returns byte location where you are in the file currently.

seek(long pointer) - Positions your file to the specified location.


import java.io.*;
public class Random {
     public static void main(String[] args) throws IOException {
        RandomAccessFile inout = new RandomAccessFile("story.dat",
           "rw");
        System.out.println("Pointer is at : " +
           inout.getFilePointer());
        inout.writeUTF("Spike is a ");
        long pointer = inout.getFilePointer();
        System.out.println("Pointer is now at : " + pointer);
        inout.writeUTF("good dog.");
        inout.seek(pointer);
        inout.writeUTF("very good dog."); // Overwrites old data
        inout.seek(0);
        System.out.println(inout.readUTF() + inout.readUTF());
        inout.close();
     }
 }


Random2.java

In the following example, use a Hex Editor to look at the output of the generated file.


import java.io.*;
public class Random2 {
   public static void main(String[] args) throws IOException {
      RandomAccessFile books = new RandomAccessFile("books.dat", "rw");
      String [] bookList = {"Starship Troopers", "The Hobbit",
        "The Man in the High Castle"};
      int [] yearList = {1959, 1954, 1974};
      for(int i=0; i<3; i++) {
         books.writeUTF(bookList[i]);
         books.writeInt(yearList[i]);
      }
      books.seek(0);
      books.readUTF();
      books.readInt();
      System.out.println(books.readUTF() + " " + books.readInt());
      books.close();
   }
}



Encode.java

The OutputStreamWriter class provides capability to encode a stream of characters into some industry standard encoding. Remember chars use 2 bytes in memory. When we write char Unicode to a file, the output depends on the specified encoding.

The OutputStreamWriter class provides the capability to specify an encoding for the output of Unicode.

Try out the 6 encodings allowed in the following program. Take a look at each generated file with a hex editor. Also use the Decode.java program to see what encoding specifications can read in the various encoding methods.


import java.io.*;
import tools.*;
  public class Encode {
     public static void main(String [] args) throws IOException {
        String[] encodings={"US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE",
                            "UTF-16LE", "CP037"};
        String[] descriptions={"7-bit ASCII", "Windows Default",
                   "8 bit Unicode Transfromation Format",
                   "16 bit Unicode Transformation Format, big-endian",
                   "16 bit Unicode Transformation Format, little-endian",
                   "USA, Canada, Netherlands, ...."};

        for (int i=0; i< encodings.length; i++)
        {
            System.out.println(i + ") "+ encodings[i] + "   " + descriptions[i]);
        }

        System.out.println("Select one of the above encodings==>");

        MyInput in = new MyInput();
        int encodeVal = in.getInt();

        System.out.println("Enter text to encode:");
        String str = in.get();

        FileOutputStream fileOut = new FileOutputStream("encode.txt");
        OutputStreamWriter outWriter = new OutputStreamWriter(fileOut, encodings[encodeVal]);
        PrintWriter out = new PrintWriter(outWriter);
        out.print(str);
        out.close();
        System.out.println("Your output is in encode.txt");
     }
}


Decode.java

To read in a character based file with respect to a specific Encoding, use the InputStreamReader class.


import java.io.*;
import tools.*;
  public class Decode {
     static void dumpFile(InputStreamReader inSrc) throws IOException
     {
        BufferedReader in = new BufferedReader(inSrc);
        String str;
        System.out.println("InputStreamReader encoding = " + inSrc.getEncoding());
        System.out.println("***************** Start Dumping");
        do
        {
            str = in.readLine();
            if (str != null)
                System.out.println(str);
        }while (str != null);
        System.out.println("****************** Done Dumping");
        inSrc.close();
     }

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

        // Use Default Encoding
        FileInputStream f1 = new FileInputStream("encode.txt");
        InputStreamReader in1 = new InputStreamReader(f1);
        dumpFile(in1);


        String[] encodings={"US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE",
                            "UTF-16LE", "CP037"};
        String[] descriptions={"7-bit ASCII", "Windows Default",
                   "8 bit Unicode Transfromation Format",
                   "16 bit Unicode Transformation Format, big-endian",
                   "16 bit Unicode Transformation Format, little-endian",
                   "USA, Canada, Netherlands, ...."};

        for (int i=0; i< encodings.length; i++)
        {
            System.out.println(i + ") "+ encodings[i] + "   " + descriptions[i]);
        }

        System.out.println("Select one of the above encodings==>");

        MyInput in = new MyInput();
        int encodeVal = in.getInt();


        // Use Selected Encoding
        FileInputStream f2 = new FileInputStream("encode.txt");
        InputStreamReader in2 = new InputStreamReader(f2, encodings[encodeVal]);
        dumpFile(in2);
     }
}


Exercises

Derive a class from the ArrayList class called ArchivedArrayList. Add to your class the name of a file that contains a saved version of your ArrayList. Add methods saveAll, readAll, read(int), save(int) . The file should be organized with a header on the front that contains the number of records and the length of a record (note we will simplify this to have fixed size records). You can fix the size of the records by only allowing fixed size character arrays instead of Strings. Other primitives like int, long, double, etc are OK.

Using the following pattern, you can get a generic solution:

interface MyRecord


interface MyRecord
{
    MyRecord create();
    void save(RandomAccessFile ran) throws IOException;
    void fetch(RandomAccessFile ran) throws IOException;
}


class Student


// The following will need to be put in a file named Student.java

public class Student implements MyRecord
{
    int age=54;
    double gpa=3.0;
    final int name_size=15;
    char[] name_arr= new char[name_size];

    Student()
    {
    }

    Student(int a, double g, String n)
    {
      // Fill In code
    }

    }
    public String toString()
    {
      // Fill in code

       return retval;
    }

    public MyRecord create()
    {
        return new Student();
    }


    public void save(RandomAccessFile ran) throws IOException
    {
        // fill in code
    }
    public void fetch(RandomAccessFile ran) throws IOException
    {
        // fill in code
    }


}



class ArchivedArrayList


class ArchivedArrayList extends ArrayList
{
    RandomAccessFile archive=null;
    long recordSize=-1;
    MyRecord factory;

    ArchivedArrayList(MyRecord f)
    {
        factory = f;
    }

    void setFile(String filename) throws IOException
    {
        archive = // Fill in code
    }
    void setSeek(int index) throws IOException
    {
       if (recordSize < 0)
            throw new IOException("Must readAll or saveAll first");

       // Fill in code
    }

    void saveAll() throws IOException
    {
        if (size() == 0) return; // Nothing to save
        // Fill in code
    }
    void save(int index) throws IOException
    {
        if (recordSize < 0)
            throw new IOException("Must readAll or saveAll first");

        // Fill in code
    }
    void readAll() throws IOException
    {
        // Fill in code
    }
    void read(int index) throws IOException
    {
        if (recordSize < 0)
            throw new IOException("Must readAll or saveAll first");

        // Fill in code
    }

    public static void main(String[] args) throws IOException
    {
        Student stud0 = new Student(55, 3.2, "Clem Hasselbach");
        Student stud1 = new Student(30, 3.8, "Prudence");
        Student stud2 = new Student(40, 3.8, "Pete");
        ArchivedArrayList arr = new ArchivedArrayList (stud1);
        arr.setFile("archive.ran");

        arr.add(stud0);
        arr.add(stud1);
        arr.add(stud2);

        arr.saveAll();


        ArchivedArrayList arr2 = new ArchivedArrayList(stud1);
        arr2.setFile("archive.ran");
        arr2.readAll();

        for (int i =0; i < arr2.size(); i++)
        {
            Student st = (Student)arr2.get(i);
            System.out.println(" arr2: " + st);
        }
    }
}




Note that you can easily add new fields to your Student class and the ArchivedArrayList still works. See if you can add height, and weight to the Student record.

Note that you can create another class like Student and again still use the Generic ArchivedArrayList. As long as a class implements MyRecord properly, ArchivedArrayList works.

Solution to in class Exercise ---- Don't peek until you have given it some good effort

Last Updated: April 1, 2003 9:04 AM