Section 12.3. Files: Places to Put Your Strings and Other Stuff


[Page 394 (continued)]

12.3. Files: Places to Put Your Strings and Other Stuff

Files are large, named collections of bytes on your hard disk. Files typically have a base name and a file suffix. The file barbara.jpg has the base name of "barbara" and a file suffix of "jpg" that tells you that the file is a JPEG picture.

Files are clustered into directories (sometimes called folders). Directories can contain files as well as other directories. There is a base directory on your computer which is referred to as the root directory. On a computer using the Windows operating system, the base directory will be something like C:\. A complete description of what directories to visit to get to a particular file from the base directory is called a path.

> String filename=FileChooser.pickAFile(); > System.out.println(filename); C:\Documents and Settings\Mark Guzdial\mediasources\640x480.jpg


The path that is printed tells us how to go from the root directory to the file 640x480.jpg in Mark's mediasources directory. We start at C:\, choose the directory Documents and Settings, then the directory Mark Guzdial, then the directory mediasources.

We call this structure a tree (Figure 12.1). We call C:\ the root of the tree. The tree has branches where there are sub-directories. Any directory can contain more directories (branches) or files, which are referred to as leaves. Except for the root, each node of the tree (branch or leaf) has a single parent branch node, though a parent can have multiple children branches and leaves.

Figure 12.1. Diagram of a directory tree.


We need to know about directories and files if we're going to manipulate files. If you're dealing with a big Web site, you are going to be working with a lot of files. If you are going to be dealing with video, you will have about 30 files (individual frames) for each second of video. You don't really want to write a line of code to open each frame! You want to write programs that will walk directory structures to process Web or video files.


[Page 395]

12.3.1. Reading from Files

To read from a file we need a class that knows how to read from a file. In Java the input and output classes are in the package java.io. A class that knows how to read from a character-based file is: FileReader. The class FileReader has a constructor that takes a file name as a string (an object of the String class).

However, we won't use this class directly. When you read from a file you are moving data from secondary storage (disk) into memory. Reading from secondary storage is much slower than reading from memory. So it is better to read a block of data from the disk into memory and then read from memory as you need the data than to read a line at a time from the disk. This is called buffering the data. We will use a class to do this for us: BufferedReader. The class BufferedReader has a constructor which will take a FileReader as a parameter.

Whenever we are working with files, things can go wrong. The file that we are trying to read from may not exist. The disk could go bad and stop working as we are reading from it. How do we deal with these problems? Java requires that the programmer write code to handle some kinds of exceptional events. We use the shorthand exception to mean exceptional event.

12.3.2. Handling Exceptions

Execution begins in a main method. The main method typically creates an object or objects and then calls at least one method. When it calls that other method, the main method probably isn't finishedthe computer has to remember where it was in the main method so that it can come back to the exact same spot. A method that is called from the main method may call other methodsagain, from somewhere in the middle of the method, so the computer has to keep track of where it was in those other methods, too. Each time a new method is called, the reminder of where the calling method stopped is added to the top of the call stack (Figure 12.2).

Figure 12.2. Showing a call stack before a method returns and after.



[Page 396]

The call stack keeps track of all the method calls between the original call to main and the currently executing method. When a method is done executing, it is removed from the top of the call stack, and execution continues from where the calling method paused. So a call stack is like a trail of bread crumbs, in that it shows how you got to the currently executing method.

If we change the getAverage method in the Student class to no longer check whether the gradeArray is null or has a length of zero, we will get a runtime exception if we execute the following main method:

public static void main (String[] args)   {     Student student1 = new Student("Barb Ericson");     System.out.println(student1);     double[] gradeArray1 = {90,88,95,96,93};     Student student2 = new Student("Mark Guzdial",gradeArray1);     System.out.println(student2);   }


This runtime exception will print out the current call stack.

> java Student java.lang.NullPointerException:   at Student.getAverage(Student.java:109)   at Student.toString(Student.java:120)   at java.lang.String.valueOf(String.java:2131)   at java.io.PrintStream.print(PrintStream.java:462)   at java.io.PrintStream.println(PrintStream.java:599)   at Student.main(Student.java:129)


Making it Work Tip: Viewing Line Numbers in DrJava

To see the line numbers in DrJava, click on EDIT, then on PREFERENCES, and then on DISPLAY OPTIONS, and check the SHOW ALL LINE NUMBERS checkbox in the PREFERENCES window.Then click on OK.


From the call stack we can see that a NullPointerException was encountered at line 109 in the getAverage method of the Student class. The getAverage method was called at line 120 of the method toString in the class String. We can continue following the call stack back to line 129 of the main method of the Student class.

When an exceptional event happens during execution of a method, an object of the class java.lang.Exception, or an object of one of the children classes of java.lang.Exception will be created (Figure 12.3). The runtime environment will look for a method in the call stack that handles this exception. Some exceptions are runtime exceptions such as ArrayIndexOutOfBoundsException. Methods are not required to handle runtime exceptions. If no method handles a runtime exception, execution will stop and information about the exception will be printed (including information about the call stack). Other exceptions must be handled or the code will not compile. These are called checked exceptions.Runtime exceptions are unchecked exceptions, meaning that they don't have to be checked for in the code in order for the code to compile.


[Page 397]

Figure 12.3. A depiction of the inheritance tree for some exception classes.


To handle checked exceptions in Java you can either catch the exception or throw it. We will cover throwing exceptions in the next chapter. To catch an exception you will use try, catch, and finally blocks. The try block is a block of code that the runtime will try to execute. The statements in the try block are executed one after another until either you reach the end of the try block or an exception occurs.

If an exception occurs during the execution of a try block, a catch block will execute. A catch block will catch objects of the specified Exception class and objects of all classes that inherit from the specified class. You can have more than one catch block following a try block. If you do have more than one catch block, be sure to catch the most specific exception before you catch a more general one.

The optional finally block is used to do clean-up no matter what happens in the try and/or catch blocks. It will always execute regardless of whether an exception occurs or not.

try {    // statements that can cause exceptions } catch (Exception ex) {    // what to do if an exception happens } finally {    // what to do no matter what happens above }


Here is an example class that has a method that will read and print the contents of a file. Notice that since we are using classes from package java.io we need to import the classes.


[Page 398]

Debugging Tip: Can't Resolve Class Names

If you get a compiler error that says it can't resolve a class name that you think should be part of the Java language, then you probably forgot to use an import statement and used just the class name. You can either use the full name for the class, which is packagename.ClassName, or you can use an import statement to import just the class (import java.io.BufferedReader), or import all classes in that package (import java.io.*). Import statements tell Java where to look for classes. They don't make your program any bigger by including code from other files. The import statements must go before the class declaration in the file.


Program 102. Sample Class to Read from a File
(This item is displayed on pages 398 - 399 in the print version)

import java.io.*; /**  * Class that allows you to easily read and print out the  * contents of a file  * @author Barb Ericson  */ public class SimpleReader {   /**    * Method to read a file and print out the contents    * @param fileName the name of the file to read from    */   public void readAndPrintFile(String fileName)   {     String line = null;     // try to do the following     try {       // create the buffered reader       BufferedReader reader =         new BufferedReader(new FileReader(fileName));       // Loop while there is more data       while((line = reader.readLine()) != null)       {         // print the current line         System.out.println(line);       }       // close the reader       reader.close();     } catch(FileNotFoundException ex) {       SimpleOutput.showError("Couldn't find " + fileName +                              " please pick it.");       fileName = FileChooser.pickAFile();       readAndPrintFile(fileName); 
[Page 399]
} catch(Exception ex) { SimpleOutput.showError("Error reading file " + fileName); ex.printStackTrace(); } } public static void main(String[] args) { SimpleReader reader = new SimpleReader(); reader.readAndPrintFile("test.txt"); } }


The method readAndPrintFile will loop reading from the BufferedReader a line at a time until the line that is returned from the method readLine is null. If the line isn't null it will be printed out. A null line will mean that we have reached the end of the current file. Once the end of the file is reached, it will close the BufferedReader, which will also close the file.

If the file isn't found, it will inform the user using a class that we developed, SimpleOutput, that makes it easy to show a dialog box to the user. Next it will use FileChooser to try to pick the file name. Then it will invoke the method again with the new file name.

If some other exception occurs during the execution of the try block, an error message will be displayed to the user using SimpleOutput and the call stack will be displayed by the Exception object using the method printStackTrace.

This method does not contain the optional finally clause. Why not? Well, we could move the reader.close() into a finally clause because we always want to close the file even if there is an exception, but the close can also cause an exception. So let's leave it in the try block.

Notice that we catch FileNotFoundException before we catch Exception. The class FileNotFoundException is a child of the class Exception, so we can't switch the order or we would never reach the second catch block. Catching Exception first would also catch FileNotFoundException since it is a child of Exception.

Let's add a static method to the Student class that will read student information from a file and create new student objects. We don't know how many Student objects we will be creating. What size array should we use? We could make it hold 35 elements, but that may be way too big or too little. It would be nice to have an array that can grow or shrink as needed to fit the data that we put in it. Java does provide this as part of the collection classes. The collection classes are classes that hold collections of objects.These classes are in package java.util.

12.3.3. Working with an ArrayList

A java.util.ArrayList object has an array that can grow or shrink as needed. It stores items in an array, so it preserves the order of elements. It keeps elements in order and allows duplicate elements.


[Page 400]

Here are some of the methods of the class ArrayList:

  • add(Object o) will add the passed object (o) to the end of the list (next open position in the array).

  • add(int index, Object o) will set the object at the given index to the passed object (o). If an object is at that index it will be moved to (index + 1). If an object was at (index + 1) it will be moved to (index + 2). So all objects at the passed index or above will change to the location specified by their (current index + 1).

  • get(int index) will return the object at the passed index in the array

  • set(int index, Object o) will set the value at this index in the array to the passed object (o). If an object is at that index it will be moved to (index + 1). If an object was at (index + 1) it will be moved to (index + 2). So, all objects at the index or above will change to the location specified by their (current index + 1).

  • size() returns the number of elements in the list

  • remove(int index) removes the object at the passed index. Any objects with a current index value greater than this will change to (current index 1).

We had used an array of Student objects in the class ClassPeriod and we had created an array of size 35. But, what if we have less students than this in the array? Then we are wasting space. But, what if we have more students than this in the class period? Then we will get an ArrayIndexOutOfBoundsException if we try to add more than 35 Student objects to the array. It would be better to use an ArrayList to hold the Student objects in the class period.

We can change the field in the ClassPeriod class from an array of Student objects to a list of Student objects:

private List studentList = new ArrayList();


You may be thinking, "Why is the type of the studentList field given as List instead of ArrayList?" We do this to make it easy to swap out one class for another. The type java.util.List is actually an interface. An interface is a special kind of class in Java that only defines public abstract methods and/or constants. Abstract methods are methods that don't have a block of code associated with them. The following is the Java 1.4 declaration of the interface java.lang.Comparable.

package java.lang; public interface Comparable {    int compareTo(Object o); }


As you can see the method compareTo doesn't have a block of code associated with it. Interfaces can only be used to declare public abstract methods and constants. Look up the interface java.util.List in the API and see what abstract methods are part of that interface. Notice that an interface can inherit from another interface. What interface does List inherit from? When an interface inherits from another interface it inherits all the methods and constants from the parent interface.


[Page 401]

A class that implements an interface will override the abstract methods and provide code for them. An object of a class that implements an interface can be declared to be of the interface type. Since the class ArrayList implements the List interface, an ArrayList object can be declared to be of type List.

The String class implements the Comparable interface. You will often implement the Comparable interface in classes you create. This allows you to use general sorting programs that only care that the objects that are being sorted are of the type Comparable. What does it mean to compare two students and decide whether one is less than, equal to, or greater than the other? You have to decide what it means when you implement the method compareTo in the Student class. You may compare based on grades or names. The String class compares the characters in the two string objects alphabetically.

Why would you want to specify the interface name as the type of a variable? There are several classes in Java that implement the interface List. If we change our minds about which actual class to use, and we have only specified the actual class one time (when we create the object) then it makes it easy to swap out the class for another class that implements the same interface.

Making it Work Tip: Use Interface Names Instead of Class Names

If you are declaring a variable for an object of a class that implements an interface, you may want to use the interface name as the type instead of the class name. This will let you use a different class in the future. This is especially true for the collection classes in package java.util. Use List instead of ArrayList, Set instead of HashSet, and Map instead of HashMap.


So interfaces make it easy to plug classes together and to swap out one class for another that implements the same interface. Think of this like LEGO™ blocks. All LEGO™ blocks have the same interface and will snap together. You can easily pull off one and replace it with another. Or, you can think of this like your computer's USB interface. The computer doesn't care what type of thing you plug into the USB port. It only cares that the thing understands the USB interface. So you can plug in a digital camera, a key drive, or a mouse.

Computer Science Idea: Decoupling Classes

One of the goals of object-oriented programming is to decouple classes. Decoupling means minimizing the number of changes you have to make to class A if you change class B and A refers to B. Interfaces let us decouple classes because one class can refer to an object of another class using the interface name as the type. This allows us to substitute class C for class B if classes B and C implement the same interface, without making many changes to class A.


We also need to change the accessors and modifiers and any other methods that used the studentArray to work with the studentList. Here are the modified methods in the ClassPeriod class.


[Page 402]

/**  * Method to get a student based on the index  * @return the student at this index  */ public Student getStudent(int index) {   return (Student) this.studentList.get(index); } /**  * Method to set the student at an index  * @param studentObj the student object to use  * @param index the index to set the student at  */ public void setStudent(Student studentObj, int index) {   this.studentList.add(index,studentObj); } /**  * Method to return the number of students in the period  * @return the number of students in the period  */ public int getNumStudents() {   int total = 0;   for (int i = 0; i >  this.studentList.size(); i++)   {     if (this.studentList.get(i) != null)       total++;   }   return total; }


Notice in the method getStudent that we have to cast the object that we get from the passed index in the list back to a Student object. Why do we have to do that? All of the collections classes in the package java.util such as List, Set, and Map hold objects. Our Student class inherits from the Object class so all Student objects are also considered objects of the Object class. When we pull objects back out of a collection class we can continue to treat them as objects of the Object class, but often we want to treat them as objects of their original class. But, the compiler doesn't know that the Object that we pull out of the collection class is a Student object. We have to tell it that it is by casting back to the Student class. As of Java 5.0 (1.5) you can use generics to tell the compiler the types of things in collection classes and then you don't need to cast the objects that you get back from collection classes to their original types. We cover generics in the next chapter.

Let's add another constructor to the ClassPeriod class that will take the teacher's name, the period number, and the name of a file that has student information in it. We will add a private method that will read the student information from the file and create the Student objects.


[Page 403]

Program 103. Constructor that Takes a File Name

/**  * Constructor that takes the teacher's name, period number,  * and a file name that contains the student information  * @param name the name for the teacher  * @param num the period number  * @param fileName the name of the file with the student  * information  */ public ClassPeriod(String name, int num, String fileName) {   this.teacherName = name;   this.periodNumber = num;   loadStudentsFromFile(fileName) }


Now we need to create the loadStudentsFromFile method that takes the file name to read from. It will read student information from a file a line at a time and create the Student objects. It will call the constructor that takes a delimited string to fill in the fields from the line. It will add the newly created Student objects to the list of students.

Program 104. Loading the Student Information from a File
(This item is displayed on pages 403 - 404 in the print version)

/**  * Method to read student information from a file and create  * a list of student objects  * @param fileName the name of the file to read from  * @param nameDelim the delimter between the name and grades  * @param gradeDelim the delimiter between the grades  */ private void loadStudentsFromFile(String fileName) {   String nameDelim = ":";   String gradeDelim = ",";   String line = null;   try {     // open the file for reading     BufferedReader reader =       new BufferedReader(new FileReader(fileName));     // loop reading from the file     while ((line = reader.readLine()) != null)     {       studentList.add(new Student(line,nameDelim,gradeDelim));     }   } catch (FileNotFoundException ex) {     fileName = FileChooser.pickAFile();     loadStudentsFromFile(fileName); 
[Page 404]
} catch (Exception ex) { System.out.println("Exception while reading from file " + fileName); ex.printStackTrace(); } }


Here is a revised main method for the ClassPeriod class that will test the new constructor that takes a file name to read the student information from.

/**  * Main method  * @param args the arguments to execution  */ public static void main(String[] args) {   ClassPeriod period =     new ClassPeriod("Ms. Clark",5,"student.txt");   // print info about the class period   System.out.println(period);   // print info for each student   for (int i = 0; i > period.studentList.size(); i++)     System.out.println("Student " + i + " is " +                        period.getStudent(i)); }


Running this main method will result in:

Class Period 5 taught by Ms. Clark with 3 students Student 0 is Student object named: Jane Dorda Average: 90.0 Student 1 is Student object named: Mike Koziatek Average: 84.84 Student 2 is Student object named: Sharquita Edwards Average: 94.0


12.3.4. Writing to a File

To read from a file we used an object of the FileReader class, along with an object of the BufferedReader class to handle the buffering of the data in memory from the disk. We will do much the same thing when we write to a file. We will use a BufferedWriter which will buffer the data until there is enough to write to the file efficiently. We will use a FileWriter to handle the actual writing of the data to a file. The class BufferedWriter has a constructor which will take a FileWriter as a parameter.

Here's an example of writing a silly file. The method newLine() will print a new line that is correct for the operating system that the program is running on. Some programs use the special character '\n', but this isn't correct for all operating systems. It is better to use the newLine() method.


[Page 405]

Program 105. Sample Class for Writing to a File

import java.io.*; /**  * Class that shows how to write to a file  * @author Barb Ericson  */ public class SimpleWriter {   /**    * Method to write a silly file    */  public void writeSillyFile()  {    try {       // try to open the buffered writer       BufferedWriter writer =         new BufferedWriter(new FileWriter("silly.txt"));       // write out the file       writer.write("Here is some text.");       writer.newLine();       writer.write("Here is some more.");       writer.newLine();       writer.write("And now we're done.");       writer.newLine();       writer.newLine();       writer.write("THE END");       writer.close();     } catch (Exception ex) {       System.out.println("Error during write of silly.txt");     }   }   public static void main(String[] args)   {     SimpleWriter writer = new SimpleWriter();     writer.writeSillyFile();   } }


Executing the main method of this class will create a file with the following contents:

Here is some text. Here is some more. And now we're done. THE END



[Page 406]

12.3.5. Generating a Form Letter

We have used the split method of the String class to separate a delimited string into parts. We can also write methods that will assemble text. One of the classic structured texts that we're all too familiar with is spam or form letters. The really good spam writers (if that's not a contradiction in terms) fill in details that actually do refer to you in the message. How do they do that? It's pretty easythey have a method that takes in the relevant input and plugs it into the right places.

Program 106. A Form Letter Generator
(This item is displayed on pages 406 - 407 in the print version)

import java.io.*; /**  * Class used to generate form letters  * @author Barbara Ericson  */ public class FormLetterGenerator {   /**    * Method to generate a form letter    * @param isMale true if this is for a male    * @param String lastName the last name for the recipient    * @param String city the name of the city for the recipient    * @param eyeColor the eye color of the recipient    */   public void writeLetter(String title, String lastName,                           String city, String eyeColor)   {     String fileName = lastName + "Letter.txt";     // try to open the file and write to it     try {       // create the buffered writer to use to write the file       BufferedWriter writer =         new BufferedWriter(new FileWriter(fileName));       // write the beginning of the letter       writer.write("Dear " + title + " " + lastName + ", ");       writer.newLine();       writer.newLine();       // write the body of the letter       writer.write("I am writing to remind you of the offer");       writer.newLine();       writer.write("that we sent to you last week.  ");       writer.write("Everyone in");       writer.newLine();       writer.write(city +                    " knows what an exceptional offer this is!");       writer.newLine(); 
[Page 407]
writer.write("(Especially those with lovely eyes of " + eyeColor + "!)"); writer.newLine(); writer.write("We hope to hear from you soon."); writer.newLine(); writer.newLine(); // write the ending writer.write("Sincerely,"); writer.newLine(); writer.write("I. M. Acrook"); // close the file writer.close(); } catch (Exception ex) { System.out.println("Error writing to " + fileName); } } public static void main(String[] args) { FormLetterGenerator formGenerator = new FormLetterGenerator(); formGenerator.writeLetter("Mr.","Guzdial","Decatur","brown"); } }


How it Works

This method takes a title, a last name (family name), a city, and an eye color as input. It opens a file with a name created by appending Letter.txt to the last name. It writes out a bunch of text, inserting the parameter values into the right places. Then it closes the BufferedWriter which will close the file.

When the main method is executed, it creates a file with the following contents.

Dear Mr.Guzdial, I am writing to remind you of the offer that we sent to you last week.  Everyone in Decatur knows what an exceptional offer this is! (Especially those with lovely eyes of brown!) We hope to hear from you soon. Sincerely, I. M. Acrook


12.3.6. Modifying Programs

Now let's start using files. Our first program will do something pretty interestinglet's write a program to change another program.


[Page 408]

Here is the program we will modify:

import java.awt.*; import java.awt.font.*; import java.awt.geom.*; /**  * Class to create a cartoon out of a picture  * @author Barb Ericson  */ public class Cartoon {   //////////// fields ///////////////////   private Picture picture;   /////////// constructor ///////////////   /**    * Constructor that takes the picture    * @param p the picture to use    */    public Cartoon(Picture p) { this.picture = p; }   /////////// methods //////////////////   /**    * Method to add a word balloon that contains the message    * @param message the text to show    * @param xPos the top left for the word balloon    * @param yPos the top left for the word balloon    */   public void addWordBalloon(String message,int xPos, int yPos)   {     // get the Graphics2D     Graphics g = this.picture.getGraphics();     Graphics2D g2 = (Graphics2D) g;     // get the font information for the message     Font font = Font("Arial",Font.BOLD,24);     FontRenderContext frc = g2.getFontRenderContext();     Rectangle2D bounds = font.getStringBounds(message,frc);     LineMetrics metrics = font.getLineMetrics(message,frc);     float lineHeight = metrics.getHeight();     float ascent = metrics.getAscent();     // draw the ellipse for the word balloon     double ellipseWidth = bounds.getWidth() * 1.5;     double ellipseHeight = bounds.getHeight() * 2.0;     g2.setColor(Color.WHITE); 
[Page 409]
g2.fill(new Ellipse2D.Double(xPos,yPos, ellipseWidth, ellipseHeight)); // draw the message centered in the ellipse float x0 = (float) ((ellipseWidth - bounds.getWidth()) / 2 + xPos); float y0 = (float) ((ellipseHeight - lineHeight) / 2 + yPos + ascent); g2.setColor(Color.BLACK); g2.setFont(font); g2.drawString(message,x0,y0); } public static void main(String[] args) { Picture picture = new Picture(FileChooser.getMediaPath("horse.jpg")); Cartoon cartoon = new Cartoon(picture); cartoon.addWordBalloon("Just Horsing Around!",42,20); picture.explore(); } }


Common Bug: Failure to Set the Media Path

Remember to set the directory for your media by executing FileChooser.setMediaPath("fullDirectoryPath/"); before you use FileChooser.getMediaPath("name.jpg");. Replace fullDirectoryPath with the full path name for the directory (like c:/intro-prog-java/mediasources/). Be sure to include a final '/' character in the directory name. If you don't set the media directory before you try to use it, the file may not be found.


Run the main method for this class. It will create a cartoon by adding a word balloon to a picture. It will display the cartoon. To modify this file we will read the Cartoon.java file, and replace the current word balloon text with different text.

To do this we will first loop reading a line at a time until we either reach the end of the file or find the text we want to replace. We can look for the text to replace using the String method indexOf. If we haven't reached the end of the file or found the text to replace, we will just add the current line to a list of lines. We can use an ArrayList to hold the list of lines.

If we stopped the first loop because we found the text to be replaced, we will replace the text and add the modified line to the list of lines. Next we need to loop till the end of the file adding each additional line to the list of lines.

At this point we have reached the end of the file and we can close the file. Then we open it again for writing and write all the lines in the list to the file. Finally, we can close the file.


[Page 410]

Program 107. A Program that Changes Another Program
(This item is displayed on pages 410 - 411 in the print version)

import java.util.*; import java.io.*; /**  * Class to demonstrate using a program to modify another program  * @author Barb Ericson  */ public class FileModifier {   /**    * Method to modify the first string in a method to    * be the passed changed text    * @param fileName the file name for the class to modify    * @param textToChange the text to change    * @param changedText the new text to use for the text to    * change    */   public void modifyFile(String fileName,                          String textToChange,                          String changedText)   {     List lineList = new ArrayList();     String line = null;     int pos = 0;     // try the following     try {       // open the file to read from       BufferedReader reader =         new BufferedReader(new FileReader(fileName));       /* loop while there are more lines in the file        * and we haven't found the text to change yet        */       while((line = reader.readLine()) != null &&             line.indexOf(textToChange) < 0)       {         lineList.add(line);       }       /* If we get there we either ran out of lines or we        * found the text to change        */       if (line != null)       {         // get the position of the text to change         pos = line.indexOf(textToChange); 
[Page 411]
// modify the string lineList.add(line.substring(0,pos) + changedText + line.substring(pos + textToChange.length())); // loop till the end of the file adding the rest while ((line = reader.readLine()) != null) { lineList.add(line); } } // now close the file reader.close(); // create a writer to write out the file BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); // loop writing out the lines for (int i = 0; i < lineList.size(); i++) { writer.write((String) lineList.get(i)); writer.newLine(); } // close the writer writer.close(); } catch (FileNotFoundException ex) { SimpleOutput.showError("Couldn't find file " + fileName); fileName = FileChooser.pickAFile(); modifyFile(fileName,textToChange,changedText); } catch (Exception ex) { SimpleOutput.showError("Error during read or write"); ex.printStackTrace(); } } // Main method to run public static void main(String[] args) { FileModifier fileMod = new FileModifier(); String file = "C:\\intro-prog-java\\bookClassesFinal\\Cartoon.java"; fileMod.modifyFile(file, "Just Horsing Around!", "What's up, Wilbur?"); } }



[Page 412]
How it Works

This program opens up the file Cartoon.java. It loops reading a line at a time from the file until it finds the text to change or reaches the end of the file. If it finds the text to change, it will replace the text with the new text and add that modified string to the list of lines. Then it will loop adding any additional lines in the file to the list of lines. After it has reached the end of the file, it will close the file and then open it up again and write all the lines in the list to the file. It will close the file again when it has finished writing all the lines.

After you execute the main method of this class, open Cartoon.java to verify that it did change the string. You should compile Cartoon.java and then run the main method to see the change. Here is what the main method will look like after it has been changed (Figure 12.4):

public static void main(String[] args) {   Picture picture =     new Picture(FileChooser.getMediaPath("horse.jpg"));   Cartoon cartoon = new Cartoon(picture);   cartoon.addWordBalloon("What's up Wilbur?",42,20);   picture.explore(); }


Figure 12.4. Original cartoon (left) and after the class has been modified (right).


This is how vector-based drawing programs work. When you change a line in AutoCAD or Flash or Illustrator, you're actually changing the underlying representation of the picturein a real sense, a little program whose execution results in the picture you're working with. When you change the line, you're actually changing the program, which is then re-executed to show you the updated picture. Isn't that slow? Thank God for Moore's Law! Computers are fast enough that we just don't notice.


[Page 413]

Being able to manipulate text is particularly important for gathering data on the Internet. Most of the Internet is just text. Go to your favorite Web page, then use the View Source option in the menu. That's the text that defines the page you're seeing in the browser. Later, we'll learn how to download pages directly from the Internet, but for now, let's assume that you've saved (downloaded) pages or files from the Internet onto your disk, and then we'll do searches from there.

For example, there are places on the Internet where you can grab sequences of nucleotides associated with things like parasites. Mark found a file that looks like this:

>Schisto unique AA825099 gcttagatgtcagattgagcacgatgatcgattgaccgtgagatcgacga gatgcgcagatcgagatctgcatacagatgatgaccatagtgtacg >Schisto unique mancons0736 ttctcgctcacactagaagcaagacaatttacactattattattattatt accattattattattattattactattattattattattactattattta ctacgtcgctttttcactccctttattctcaaattgtgtatccttccttt


Let's say that we had a subsequence (like "ttgtgta") and we wanted to know which parasite it was part of. The parasite information is between the '>' and the end of the line containing the '>'.

We can loop reading this file a line at a time until we find the sequence. We can append each line to a string and add a new line character '\n' to the string to help determine the end of each line. Once we find the sequence we can look backward in the string for the last '>' character. Then we can look forward from that index to the next new line character ('\n'). The parasite information will be the characters between the two found indices.

Program 108. Finding a Subsequence in Parasite Nucleotide Sequences
(This item is displayed on pages 413 - 415 in the print version)

import java.io.*; /**  * Class that searches a file for a given sequence and reports  * on the name where that sequence was found  * @author Barb Ericson  */ public class SequenceSearcher {   /**    * Method to search for a given sequence and then    * report on the name    */   public String getNameForSequence(String fileName, String seq)   {     String info = "";     String line = null;     String name = null; 
[Page 414]
// try the following try { // read from the file BufferedReader reader = new BufferedReader(new FileReader(fileName)); // loop till end of file or find sequence while ((line = reader.readLine()) != null && line.indexOf(seq) < 0) { // add to string with new line character info = info + line + "\n"; } // if get here either end of line or we found the sequence if (line != null) { // look backward for the last > int firstIndex = info.lastIndexOf('>'); // look forward from the > for the new line character int secondIndex = info.indexOf('\n',firstIndex); // get the name between the > and new line name = info.substring(firstIndex+1,secondIndex); } } catch (FileNotFoundException ex) { SimpleOutput.showError("Couldn't find file " + fileName); fileName = FileChooser.pickAFile(); getNameForSequence(fileName,seq); } catch (Exception ex) { SimpleOutput.showError("Error during read or write"); ex.printStackTrace(); } return name; } public static void main(String[] args) { SequenceSearcher searcher = new SequenceSearcher(); String fileName = FileChooser.getMediaPath("parasites.txt"); String seq = "ttgtgta"; String name = searcher.getNameForSequence(fileName,seq); if (name == null) System.out.println("The sequence " + seq + " wasn't found in " + fileName); else
[Page 415]
System.out.println("The sequence " + seq + " was found in " + name); } }


How it Works

The method getNameForSequence takes a file name to search and the sequence to search for. It will loop through the lines in the file until it finds the sequence to search for. Each line read from the file is appended to the string referred to by the variable info along with a new line character ('\n'). When the sequence that we are searching for is found we look backward in the info string for the last '>'. Then we look forward from that index till we find the new line character. The name is the substring between the two found indices.

There are programs that wander the Internet, gathering information from Web pages. For example, Google's news page (http://news.google.com) isn't written by reporters. Google has programs that go out and snag headlines out of other news sites. How do these programs work? They simply download pages from the Internet and chop out the desired pieces.

For example, let's say that you wanted to write a function that would give you the current temperature by reading it off a local weather page. In Atlanta, a good place to find the current weather is http://www.ajc.com/weatherthe weather page of the Atlanta Journal-Constitution. By viewing source, we can find where the current temperature appears in the page, and what the key features of the text are around it to grab just the temperature. Here's the relevant part of the page that Mark found one day:

<td ><img src="/books/1/79/1/html/2//shared-local/weather/images/ps.gif" width="48" height="48" border="0"><font size=-2><br></font><font size="-1" face="Arial, Helvetica, sans-serif"><b>Currently</b><br> Partly sunny<br> <font size="+2">54<b>\&deg;</b></font><font face="Arial, Helvetica, sans-serif" size="+1">F</font></font></td> </tr>


You can see the word Currently in there, then the temperature just before the characters <b>&deg. We can write a program to chop out those pieces and return the temperature, given that the weather page is saved in a file named ajc-weather.html. This program won't always work. The page format will change and the key text we're looking for might move or disappear. But as long as the format is the same, this recipe will work.

Program 109. Get the Temperature from a Weather Page
(This item is displayed on pages 415 - 417 in the print version)

import java.io.*; /**  * Class to find the temperature in a web page.  * @author Barb Ericson  */ 
[Page 416]
public class TempFinder { /** * Method to find the temperature in the passed * file * @param fileName the name of the file to look in */ public String getTemp(String fileName) { String seq = "<b>&deg"; String temp = null; String line = null; // try the following try { // read from the file BufferedReader reader = new BufferedReader(new FileReader(fileName)); // loop till end of file or find sequence while ((line = reader.readLine()) != null && line.indexOf(seq) < 0) {} // if there is a current line if (line != null) { // find the temperature int degreeIndex = line.indexOf(seq); int startIndex = line.lastIndexOf('>',degreeIndex); temp = line.substring(startIndex + 1, degreeIndex); } } catch (FileNotFoundException ex) { SimpleOutput.showError("Couldn't find file " + fileName); fileName = FileChooser.pickAFile(); temp = getTemp(fileName); } catch (Exception ex) { SimpleOutput.showError("Error during read or write"); ex.printStackTrace(); } return temp; } public static void main(String[] args) { TempFinder finder = new TempFinder(); String file = FileChooser.getMediaPath("ajc-weather.html"); String temp = finder.getTemp(file); if (temp == null) System.out.println("Sorry, no temp was found in " + file);
[Page 417]
else System.out.println("The current temperature is " + temp); } }


How it Works

This function assumes that the file ajc-weather.html is stored in the media folder specified with setMediaPath. The method getTemp opens the file and reads a line at a time until it reaches the end of the file or finds the sequence. If we find the sequence we save the position where we found it. Then we look backward from there for the index of the last '>'. The temperature is between these two indices.

If we run the main method of this class we get:

> java TempFinder The current temperature is 54




Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
ISBN: N/A
EAN: N/A
Year: 2007
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net