Recent Changes - Search:

Java Tutorial

edit SideBar

Files

These pages make up the course notes for my Java programming course (run at Dallam, Milnthorpe, Cumbria). Hopefully they also make a useful self-learning tutorial.

via a diversion around the command line

A lot of programs need to load and store data. The way we do this on pretty much all modern computers is using Files (and folders or directories). In the previous section we looked at classes and objects and I mentioned that Files were a class from which we can create objects.

Lets have a look at some things we can do with files. We will start off by creating a class that gives some details about files. It would be useful if we could use this class without having to edit it each time we want to show the details of a different file (think back to the VatBill example, initially we change the values by editing the code, then we added a pop-up dialogue to collect information). For this class we are going to use Command Line Arguments to pass the information in. We will discuss why later.

Create a new class called FileDetails and put it in a package called tutorial.files

You can create the package in the File->New->Class dialogue

Now paste the following code


package tuorial.files;

import java.io.File;
import java.io.IOException;

public class FileDetails {

        public void displayFileInfo(String filePath){
                File file = new File(filePath);

                try {
                        if (file.exists()) {
                                displayString("Details for file " + file.getName());
                                displayString("in folder " + file.getCanonicalPath());
                                displayString("\t Last Modified " + file.lastModified() );
                                displayString("\t Size (bytes) " + file.length());
                        }
                        else {
                                displayString("File not found");
                        }
                }
                catch (IOException ioe) {
                        ioe.printStackTrace();
                }

        }

        private void displayString(String text) {
                System.out.println(text);
        }

        public static void main(String[] args){

                FileDetails fileDetails = new FileDetails();
                fileDetails.displayFileInfo(args[0]);
        }

}


Try running this (right click the FileDetail class and choose Run As - > Run).

You should get an error
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException?: 0

	at tuorial.files.FileDetails?.main(FileDetails?.java:39)

The message indicates that the error is on line 39 (in my code, yours may be different), looking at line 39 in my code:

The error message says ArrayIndexOutOfBoundsException and as we saw in a previous section arrays are denoted by square brackets - [] So it's a fair guess that the problem is with args[0]

We have created lots of main methods and they always look like

    public static void main(String[] args)

We now know that public means that any one can use the method, static means that we can use it without first making an object from the class, the method is called main, so what is the String[] args part?

If you have ever run a command from the command line (DOS prompt, shell prompt, etc.) you have most probably used command line arguments, e.g. something like dir \windows (windows) or ls /home (Linux, Unix) Try It. In the windows example dir is the command (a program) and \windows is the "argument" to the command. In other words a command line argument is a piece of information passed to a program when it is executed.

Now you may be thinking that this is not very useful, however it is a very common technique. Arguments can be passed to programs in short cuts and in batch/script files as well as on the command line. Sometimes it is not very convenient to have to type something in every time a program runs (especially if it is a long piece of text, such as the path to a file or directory) and it is better to have it stored in a short-cut or batch file. Software developers tend to use the command line quite a lot - it is a powerful tool that you should get to know.

But... we are not running this from the command line, we are running it from within the IDE, so how do we tell the IDE what we want to pass to the program? Right click the FileDetails class and choose Run As -> Run which will bring up the following dialog.

In the arguments box enter the name of a file - remember to include the full path. I have choosen to use the VatBill.java file but you can pick any file. Use windows explorer (or your systems file browser) to check the path of the file if you are unsure. Click Apply and then Run. The program should run and display some details about the file.

If you want you can also try running from the command line

Try looking at the Specification for the File class http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html and add some more file details to the output (e.g. canWrite, isFile).

Try using the input pop-up to get the file name rather than using the command line

Try making it work with either the command line or the input pop-up (hint - if there is no command line argument then display the pop-up)

Reading and Writing Files

It's all very well getting details of a file, but things get a bit more useful when we can read data from, and write data to files. Before we get into the nitty-gritty we need to have a bit of an understanding of what may be in a file. At a basic level there are two types of file, Text (also called ASCII) and Binary.

A Text file contains character data represented by a standard encoding system (for example American Standard Code for information Interchange - ASCII, or may be another encoding system like Unicode). These are the sort of files we create to hold our source code, or that you create with Notepad or other text editors.

A Binary file contains data that may be in any form, but which is not necessarily decodable into Character Strings. Executable files, files created by most "Office" applications, PDFs?, etc, are all binary files. Try opening a word-processor file with a text editor (such as Notepad), you may well see that there are a lot of strange characters in there. You may also spot some readable text.

Normally it is easier to develop programs that read and write Text files, and that is what we are going to do from here on.

Reading files
One thing that is useful is to be able to search our source code for specific text. You may remember using a particular piece of code but cannot remember which file it is in. The operating system and the IDE have ways of searching files, but they do not give "context" - i.e. they do not show a few lines before and after the text which is useful in determining exactly which file you want. (Linux/Unix have grep to do this, bear with me if you are familiar with that, it's a good example).

So lets state clearly what we want to do and then think about how we want to do it. We want to:

  • Open each file in a directory
  • Look for a specific String of characters within the file
  • If we find the String display a few lines before where the string was found and a few lines after

Is that everything we need to achieve? see if you can think of any other things we may need to consider

For now let's write something that meets the requirements listed above. It may be better to start of with a more detailed set of requirements but, this is a learning process. It is also often good to start simply and build up, even if this means rewriting some code. In anything but the most trivial system, you are unlikely to be able think of all the nuances before writing a line of code. As you get more experience, you will be able to state the requirements for a program more comprehensivly.

Create another class and paste in the following to get started Remeber to use the correct package and class

package tuorial.files;

import java.io.*;

public class ContextSearch {

        /**
        * run through all files in a directory
        *
        * @param path - the path to the directory to process
        */
        public void processDirectory(String path, String text) {

                File dir = new File(path);
                for (String fileName : dir.list()) {
                        searchFile(fileName, text);
                }

        }

        /**
        * search a for a text string
        *
        * @param file - the file to read
        * @param text - the text to find
        */
        public void searchFile(String file, String text) {
                try {
                        BufferedReader in = new BufferedReader(new FileReader(file));
                        String line;
                        while ((line = in.readLine()) != null) {
                                if (line.indexOf(text) > -1) {
                                        System.out.println("Found: " + line + " in " + file);
                                }
                        }
                        in.close();
                } catch (IOException e) {
                        //let everyone know there was a problem
                        e.printStackTrace();
                }

        }


        /**
        * @param args
        */
        public static void main(String[] args) {

                ContextSearch cs = new ContextSearch();

                cs.processDirectory(args[0], args[1]);

        }

}


Try running it on some files - I suggest you use the source code you have created for this tutorial. Remember to set up the command line arguments as described above. I used the path to my "tutorial" source folder and "Vat" as the command line arguments as shown below (there is a space between D:\_work\java\tutorial\ and Vat

On running it I got the following error and output - your may be different.

What do you think has gone wrong?

First lets consider the error, on looking at the contents of the directory being searched I notice that it has a sub-directory which is also called "tutorial". The error says Access Denied but I suspect that that message is a bit misleading, I'm guessing that we tried to open a directory as a file and the Operating System prevented us from doing that - hence the Access Denied.

Also there are a lot of odd characters in the output. Notice that these are associated with .class files. When we compile a java program (something that Eclipse automatically does for us) the source code, in a file with a .java extension is compiled into a file with a .class extension. These files contain binary data, and as discussed above, this data is not generally readable as character strings.

Think about how to fix these problems
I am not going to give you a complete solution to this exercise as the only way to really learn is to do it for yourself. However here are a few pointers

  • The File class (http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html) has methods to tell if a File is a directory.
  • The binary data problem is a little more tricky, but as a start point the Characters in a file can be represented by numbers (in fact that's what ASCII does, it specifies which characters are represented by which numbers). ASCII is designed so that all human readable Latin characters and punctuation (a-z, A-Z, 0-9, ,.;:()[] etc, i.e. everything you see on your keyboard) is between 32 and 127 (32 is Space), with extended characters above 127. Below 32 are "control" characters such as Tab, Line Feed, Carriage Return, etc. Now, we would only expect to see characters above 32 plus a few control characters (as Tab, Line Feed, Carriage Return) in an ASCII file. We would never expect to see characters represented by 0,1,2,3,4,5,6,7,8 in there. So testing for these is a way to determine if a file is binary or not.
  • The next question to ask yourself is how do you want to handle a binary file? You could ignore it altogether, but sometimes it is useful to know if a string is in a binary file, for example Word Processor files are binary but can be searched for text. So you may want to just output a message indicating that the file contained the string without outputting the context.

Binary Data Hint


Writing

The final thing we need to know about files is how to write data to them. This is basically the only way your application can store data permanently. So if you want to keep some information so that it can be accessed later (after the program has been closed, the system shut down, etc), you need to write it to a file. The process is structurally similar to reading a file, i.e. Open the file, write to it, and close it. We can open a file for writing or appending, writing always creates a new file (and overwrites any existing file of the same name) appending adds to the end of the file.

In the example above, we created a useful little utility program that finds text in a file and prints it out with some context. Let's have a go at creating another little utility. I often find myself working on many different computers, in different places, on different networks. What would be nice is to have all my favourite webpage links stored on a USB Memory Stick so that I can take them around with me. (I know there are on-line book mark services, but you have to log in, and remember the url of the on-line service, etc.)

It turns out that a web-page is just an ASCII file with some special "Mark Up" in it. So if I save my book marks to a file in the correct format it will open in the Web Browser and I will have a page of links. I can then just click a link and go to that site. What is needed is an easy way of adding links to the file.

To illustrate this, copy the HTML below and paste it into a file called bookmarks.hmtl (You can use Eclipse to do this, or a text editor like notepad).

<html>
   <head>
      <title>My Bookmarks</title>
   </head>
   <body>
      <a href="http://www.google.co.uk">Google</a>
      <a href="http://www.bbc.co.uk">BBC</a>
   <body>
</html>

Now find the file - where ever you saved it - in the file system Explorer (e.g. windows explorer) and double click it, because we saved it with an extension of .html it should open with your web browser and you should be able to click the links and go to the sites.

That's ok, but it is a bit cumbersome to have to type all the <a href=" ... stuff each time. If we could pop-up a window, enter the text and save it with just copy and paste then life would be a lot simpler.

To split the task up a little, there are two things we need to do:

  • Get some input from the user
  • Append the input to the end of the bookmarks.html file in the correct format

If you have been working through this tutorial you will remember that we used a pop-up input box in the VatBill example. As a start, we can re-use that pop-up to collect the user input. We will need to write some code to handle the file writing.

To make things simple make sure you have a copy of the InputPopup (see MoreClasses) in the package tutorial.files. In Eclipse you can right-click on the InputPopup class and copy it, and then paste it into the tutorial.files package. Eclipse will sort out the package name for you.

Now create a new class called Bookmarks, again set the package to tutorial.files. Here's how I began to code the Bookmarks class:

package tuorial.files;

public class Bookmarks {

        public String url;
        public String title;
        public String file;

        /**
        * constructor, set up a default file name if none supplied
        *
        * @param file - the name of the bookmark file or null to default
        */
        public Bookmarks(String file) {
                if (file == null) {
                        this.file = "Bookmarks.html";
                }
                else {
                        this.file = file;
                }


        }

        /**
        * add a bookmark to the file
        *
        */
        public void addBookMark() {

        }

        /**
        * prompt the user for input
        *
        */
        public void promptUser() {
                this.url = InputPopUp.prompt("Enter the URL");
                this.title = InputPopUp.prompt("Enter the Title (or leave blank)");
        }

        public static void main(String[] args) {
                String fileArg=null;
                if (args.length > 0) fileArg=args[0];
                Bookmarks bookmarks = new Bookmarks(fileArg);
                bookmarks.promptUser();
                bookmarks.addBookMark();
        }
}

Most of the code above deals with creating a bookmarks object from the Bookmarks class, and assiging a name for the Bookmarks.html file. Note it uses a Constructor which we haven't discussed yet, more on that later. I have also included a promptUser method that pops-up a box for the URL and a box for the title (obviously the URL can be copied and pasted from the browser). We have to do this as two separate boxes since we can only input one variable at once (we will fix this when we look at Swing).

So what we need to do now is fill in the addBookMark method. To do this we need to open a file for Append - i.e. to add things to the end of it, rather than overwrite it.

Firstly we need to open the file for apending. There are a confusing number of classes in the java.io package for reading and writing files. We are going to use the FileWriter class and following the recommendations we are going to "wrap" this in a BufferedWriter (this is not critical for what we are doing but it is a good habit). It is also worthing thinking about what happens if things go wrong - as we saw before Java "throws" exceptions. File reading and writing is somewhere where we might expect exceptions so we might as well start with a try - catch block to handle them. The documentation for FileWriter tells me that I need to handle an IOException.

So here is the initial code that opens the file for append and catches any exceptions should it fail to open.

try {
        //open the file for appending
        BufferedWriter out = new BufferedWriter(new FileWriter(this.file, true));

} catch (IOException e) {
        e.printStackTrace();
}

Having opened the file we need to write something to it. Stop and think about what we need to write and have a go at writing some code to do this. Things to think about are:

  • What if the title is null?
  • What do we need, apart from the URL and title?

Having done that we need to close the file which we do with:
=javascript out.close()

If we don't close the file, then the things we tried to write may not actually be saved.

Here is what my completed method looked like:

try {
        //open the file for appending
        BufferedWriter out = new BufferedWriter(new FileWriter(this.file, true));

        //if the title is blank (null) use the url as the title
        String myTitle = this.title;
        if (this.title == null ) myTitle = this.url;

        String link = "<a href='" + this.url + "'>" + myTitle + "</a><br>";
        out.write(link);
        //append a new line character
        out.newLine();
        out.close();
} catch (IOException e) {
        e.printStackTrace();
}
 

Creative Commons License

This work is Copyright Chris Hunter 2007, you may use it for non-commercial purposes
under the Creative Commons license Creative Commons Attribution-Noncommercial-Share Alike.

Edit - History - Print - Recent Changes - Search
Page last modified on July 18, 2007, at 02:01 PM