Transcript Document

CS203 LECTURE 3

John Hurley Cal State LA

Binary File I/O

Real world applications usually use databases to store large amounts of information

Main exceptions are apps that have unique file formats that can’t easily be translated to databases, like word processors or paint software.

If you have not studied text file I/O, take a look at the material in the textbook or my lectures from CS202 (same URL as this class’ website, except for the course number.)

There is a very nice way to store data from OOP applications directly to disk, though: binary file i/o

Binary File I/O

Binary files store data as sequences of bytes in a format that is usually far more compact than text files.

If you open a binary file in a word processor or text editor, you will see gibberish. It is possible to see the byte values in base 16 using a hex editor, but they will normally be unintelligible.

Every application formats its binary files differently. The only way to easily retrieve the data is by using an application that can parse the data, normally the app that generated the file in the first place.

Programming languages typically offer easy ways to store data in binary files.

Java and other object-oriented languages can format objects as binary sequences for file storage. They also offer automated ways to recreate objects from the data.

Binary File I/O

Output:

FileOutputStream is a stream for output to a file.

BufferedOutputStream is a stream with a buffer (write data in chunks for more efficiency, since there are fewer calls to the expensive input/output methods used by the operating system). BufferedOutputStream wraps an unbuffered stream such as a FileOutputStream to provide the buffer.

DataOutputStream provides methods for writing primitive types as well as Strings to the stream it wraps.

Methods like writeDouble() output the stated types All of these have equivalent input methods

The following example stores low-level types like Double and uses parsing code when the file is read, but you will soon see an example that stores and retrieve collections of objects of programmer-defined classes.

package menudemo;

// http://docs.oracle.com/javase/tutorial/essential/io/examples/DataStreams.java

import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreams { static final String dataFile = "invoicedata"; static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { "Java T-shirt",

"Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain" };

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

DataOutputStream out =

null; try {

out =

new DataOutputStream(new

BufferedOutputStream(

new FileOutputStream(dataFile))); for (int i = 0; i < prices.length; i ++) {

out.writeDouble(

prices[i]);

out.writeInt(

units[i]);

out.writeUTF(

descs[i]);

} }

finally {

out.close(); }

DataInputStream in =

null; double total = 0.0; try {

in =

new DataInputStream(new

BufferedInputStream(

new FileInputStream(dataFile))); double price; int unit;

String desc; } }

try { while (true) {

price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.

out.format("You ordered %d units of %s at $%.2f%n",

unit, desc, price); total += unit * price; } }

catch (EOFException e) {

// this is really how Oracle's documentation suggests you find the end of a file!

System.

out.println("EOF reached");}

System.

out.format("For a TOTAL of: $%.2f%n", total);

}

finally {

in.close(); }

Binary I/O

• ObjectOutputStream and ObjectInputStream allow binary I/O of whole objects • Objects read from files must be cast correctly • Objects stored this way must be

serializable,

meaning they must implement the Serializable interface, which just identifies them as convertible to a binary stream. Serializable does not require any actual methods.

• If the object contains references to other objects, all the classes must be serializable!

• Key methods are writeObject() and readObject(). readObject() requires a cast, so you have to know what kind of objects are stored in the file you are reading.

}

Monster Herd Persister

package monsterpersistence; import java.io.Serializable; public class Monster implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private String name; private String hometown; private String rampageBehavior; public Monster(String nameIn, String hometownIn, String rampageBehaviorIn){ name = nameIn; hometown = hometownIn; rampageBehavior = rampageBehaviorIn; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHometown() { return hometown; } public void setHometown(String hometown) { this.hometown = hometown; } public String getRampageBehavior() { return rampageBehavior; } public void setRampageBehavior(String rampageBehavior) { this.rampageBehavior = rampageBehavior; } public String toString (){ return name + " from " + hometown + " likes to " + rampageBehavior; }

}

Monster Herd Persister

package monsterpersistence; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class MonsterHerd implements Serializable{ private static final long serialVersionUID = 1L; private List monsters = new ArrayList(); } public List getMonsters() { return monsters; public void addMonster(Monster m){monsters.add(m);} } public String toString(){ StringBuilder sb = new StringBuilder(); for(Monster m: monsters) sb.append(m + "\n"); return sb.toString();

package monsterpersistence; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; import java.util.List; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; public class MonsterGUI { MonsterHerd herd; JFrame frame = null; JPanel panel; JMenuBar menubar; JScrollPane scrollPane; JTable table; public MonsterGUI() { } public final void initUI() { herd = new MonsterHerd(); initUI(); frame = new JFrame(); panel = new JPanel(); table = setUpTable(); menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); final JFileChooser fc = new JFileChooser(); // enums like KeyEvent evaluate to ints createAndAddMenuItem(file, "Open", KeyEvent.VK_O, "Open File", new ActionListener() { public void actionPerformed(ActionEvent event) { int retVal = fc.showOpenDialog(frame); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); MonsterHerdPersister p = new MonsterHerdPersister(); herd = p.readHerdFromFile(selectedFile); updateTable(); } } }); createAndAddMenuItem(file, "Add Monster", KeyEvent.VK_A, "Add Monster", new ActionListener() { public void actionPerformed(ActionEvent event) { String name = JOptionPane.showInputDialog(frame, "please enter the monster's name"); String hometown = JOptionPane.showInputDialog(frame, "please enter the monster's hometown"); String rampage = JOptionPane .showInputDialog(frame, "what does the monster do when it goes on a rampage?"); Monster m = new Monster(name, hometown, rampage); herd.addMonster(m); updateTable(); } }); createAndAddMenuItem(file, "Save", KeyEvent.VK_S, "Save File", new ActionListener() { public void actionPerformed(ActionEvent event) { int retVal = fc.showOpenDialog(frame); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); MonsterHerdPersister p = new MonsterHerdPersister(); p.saveHerdToFile(selectedFile, herd); } } });

} createAndAddMenuItem(file, "Exit", KeyEvent.VK_E, "Exit application", new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); menubar.add(file); frame.setJMenuBar(menubar); frame.setTitle("Monsters"); frame.setSize(600, 200); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); scrollPane = new JScrollPane(table); panel.add(scrollPane); frame.add(panel); frame.setVisible(true); } private void createAndAddMenuItem(JMenu menu, String message, int mnemonic, String tooltip, ActionListener a) { JMenuItem menuItem = new JMenuItem(message); menuItem.setMnemonic(mnemonic); menuItem.setToolTipText(tooltip); menuItem.addActionListener(a); menu.add(menuItem); } private JTable setUpTable(){ String[] columnNames = { "Name", "Hometown", "Rampage Behavior"}; Object[][] data = listToArray(herd.getMonsters()); JTable newTable = new JTable(data, columnNames); newTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); newTable.setFillsViewportHeight(true); newTable.setAutoCreateRowSorter(true); newTable.doLayout(); return newTable; } private void updateTable() { for (Monster m : herd.getMonsters()) System.out.println(m); panel.removeAll(); table = setUpTable(); scrollPane = new JScrollPane(table); panel.add(scrollPane); panel.doLayout(); } private Object[][] listToArray(List list) { Object[][] array = new Object[list.size()][3]; for (int counter = 0; counter < list.size(); counter++) { array[counter][0] = list.get(counter).getName(); array[counter][1] = list.get(counter).getHometown(); array[counter][2] = list.get(counter).getRampageBehavior(); } return array; } public static void main(String[] args) { MonsterGUI ex = new MonsterGUI(); }

Monster Herd Persister

package monsterpersistence; } import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MonsterHerdPersister{ public void saveHerdToFile(File f, MonsterHerd mh) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream( new FileOutputStream(f))); out.writeObject(mh); out.close(); } } catch (Exception e) { e.printStackTrace(); } public MonsterHerd readHerdFromFile(File f) { ObjectInputStream in = null; MonsterHerd m = null; try { in = new ObjectInputStream(new BufferedInputStream( new FileInputStream(f))); m = (MonsterHerd) in.readObject(); in.close(); } } catch (Exception e) { e.printStackTrace(); return m; }

Binary I/O With List

• Binary I/O can be used to store and retrieve lists of objects.

Binary I/O With List

package vampirebinaryio; } import java.io.Serializable; public class Crypt implements Serializable { private String location; } public Crypt(String location) { this.location = location; } public void setLocation(String location) { this.location = location; } public String getLocation() { return location; } public String toString(){ return "a really mysterious crypt in " + location;

package vampirebinaryio; import java.io.Serializable; import javax.swing.JOptionPane; public class Vampire implements Serializable { private String name; private Crypt crypt; public Vampire(String name, String location) { this.name = name; crypt = new Crypt(location); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setLocation(String location) { crypt.setLocation(location); } public String getLocation() { return crypt.getLocation(); } public void rampage() { JOptionPane.showMessageDialog(null, name + " arises from " +crypt.toString() + " and sucks people's blood " + "all night, then returns to a coffin to hide from sunlight"); } public String toString(){ return name + " inhabits " + getLocation(); } }

Binary I/O With List

package vampirebinaryio; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.swing.JFileChooser; import javax.swing.JOptionPane; public class VampireIODriver { private static List vampires; } public VampireIODriver() { vampires = new ArrayList(); public void prowl() { String[] options = { "Quit", "List Vampires", "Add A Vampire", "Save To File", "Load From File" }; int choice = 0;

Binary I/O With List

do { choice = JOptionPane.showOptionDialog(null, "Next Action", "Next Action", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); switch (choice) { case 1: listVampires(); break; case 2: addVampire(); break; case 3: saveToFile(); break; case 4: loadFromFile(); break; } } while (choice != 0); }

} public void listVampires() { StringBuilder sb = new StringBuilder(); for (Vampire v : vampires) sb.append(v.toString() + "\n"); if (sb.length() == 0) sb.append("no vampires"); JOptionPane.showMessageDialog(null, sb); } public void addVampire() { String name = JOptionPane .showInputDialog("Please enter the vampire's name"); String location = JOptionPane .showInputDialog("Please enter the vampire's location"); vampires.add(new Vampire(name, location)); } public void saveToFile() { VampirePersister persister = new VampirePersister(); JFileChooser fc = new JFileChooser(); } int retVal = fc.showOpenDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); persister.saveListToFile(selectedFile, vampires);

}

Binary I/O With List

public void loadFromFile() { VampirePersister persister = new VampirePersister(); JFileChooser fc = new JFileChooser(); int retVal = fc.showOpenDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); vampires = persister.readListFromFile(selectedFile); } } } public static void main(String[] args) { VampireIODriver d = new VampireIODriver(); d.prowl();

Binary I/O With List

package vampirebinaryio; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; public class VampirePersister implements Serializable{ public void saveListToFile(File f, List vl) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream( new FileOutputStream(f))); out.writeObject(vl); out.close(); } catch (Exception e) { e.printStackTrace(); } }

}

Binary I/O With List

} public List readListFromFile(File f) { ObjectInputStream in = null; List vl = null; try { in = new ObjectInputStream(new BufferedInputStream( new FileInputStream(f))); vl = (List) in.readObject(); in.close(); } } catch (Exception e) { e.printStackTrace(); return vl;