Working with Files
In this chapter, you discover the ins and outs of working with files and directories. I don't show you how to read or write files, but you do find out how to find files on the disk, how to create, delete, or rename files, and how to work with directories. You find out how to use the Swing file chooser dialog box that lets you add filing capabilities to Swing applications. And finally, you find out how to retrieve parameters from the command line, a useful technique because command-line parameters are often used to pass file information to console programs.
Using the File Class
The File class is your key to processing files and directories. A File object represents a single file or directory. Note that the file or directory doesn't actually have to exist on disk. Instead, the File object represents a file that may or may not actually exist.
TECHNICAL STAUFF |
Java uses a single class to represent both files and directories because a directory is actually nothing more than a special type of file. I suppose the designers of Java could have created a separate Directory class to represent directories, but using one class to represent both has its advantages. |
The File class is in the java.io package, so any program that uses it should import java.io.File or java.io.*.
Table 1-1 lists the main constructors and methods of the File class.
Table 1-1: The File Class
Constructor |
Description |
---|---|
File(String pathname ) |
Creates a file with the specified pathname. |
Field |
Description |
---|---|
String separator |
The character used to separate components of a pathname on this system; usually or /. |
Method |
Description |
---|---|
boolean canRead() |
Determines whether the file can be read. |
boolean canWrite() |
Determines whether the file can be written. |
boolean createNewFile() |
Creates the file on disk if it doesn't already exist. Returns true if the file was created, false if the file already existed. Throws IOException. |
boolean delete() |
Deletes the file or directory. Returns true if the file was successfully deleted. |
boolean exists() |
Returns true if the file exists on disk, false if the file doesn't exist on disk. |
String getCanonicalPath() |
Returns the complete path to the file, including the drive letter if run on a Windows system. Throws IOException. |
String getName() |
Gets the name of this file. |
String getParent() |
Gets the name of the parent directory of this file or directory. |
File getParentFile() |
Gets a File object representing the parent directory of this file or directory. |
boolean isDirectory() |
Returns true if this File object is a directory, false if it is a file. |
boolean isFile() |
Returns true if this File object is a file, false if it is a directory. |
boolean isHidden() |
Returns true if this file or directory is marked by the operating system as hidden. |
long lastModified() |
Returns the time the file was last modified, expressed in milliseconds since 0:00:00 AM, January 1, 1970. |
long length() |
Returns the size of the file in bytes. |
String[] list() |
Returns an array of String objects with the name of each file and directory in this directory. Each string is a simple filename, not a complete path. If this File object is not a directory, returns null. |
File[] listFiles() |
Returns an array of File objects representing each file and directory in this directory. If this File object is not a directory, returns null. |
static File[] listRoots() |
Returns an array that contains a File object for the root directory of every file system available on the Java runtime. Unix systems usually have just one root, but Windows systems have a root for each drive. |
boolean mkdir() |
Creates a directory on disk from this File object. Returns true if the directory was successfully created. |
boolean mkdirs() |
Creates a directory on disk from this File object, including any parent directories that are listed in the directory path but don't already exist. Returns true if the directory was successfully created. |
boolean renameTo (File dest) |
Renames the File object to the specified. destination File object. Returns true if the rename was successful |
boolean setLastModified (long time) |
Sets the last modified time for the File object. Returns true if the time was successfully set. |
boolean setReadOnly() |
Marks the file as read-only. Returns true if the file was successfully marked. |
String toString() |
Returns the pathname for this file or directory as a string. |
Creating a File object
To create a File object, you call the File constructor, passing a string representing the filename of the file as a parameter. Here's an example:
File f = new File("hits.log");
Here the file's name is hits.log, and it lives in the current directory, which is usually the directory from which the Java Virtual Machine was started.
If you don't want the file to live in the current directory, you can supply a complete pathname in the parameter string. However, you're now entering one of the few areas of Java that becomes system-dependent, because the way you write pathnames depends on the operating system you're using. For example, c:logshits.log is a valid pathname for Windows systems, but not on Unix or Macintosh systems (which don't use drive letters and use forward slashes instead of backslashes to separate directories).
Tip |
If you hard-code pathnames as string literals, remember that the backslash character is the escape character for Java strings. Thus, you must code two slashes to get one slash into the pathname. For example, you must code the path c:logshits.log like this: String path = "c:\logs\hits.log"; |
Creating a file
Creating a File object doesn't create a file on disk. Instead, it creates an in-memory object that represents a file or directory that may or may not actually exist on disk. To find out if the file or directory exists, you can use the exists method. For example:
File f = new File(path); if (!f.exists()) System.out.println ("The input file does not exist!");
Here an error message is displayed on the console if the file doesn't exist.
To create a new file on disk, first create a File object with the filename you want to use. Then use the createNewFile method, like this:
File f = new File(path); if (f.createNewFile()) System.out.println("File created."); else System.out.println("File could not be created.");
Note that the createNewFile method returns a boolean that indicates whether or not the file was successfully created. If the file already exists, createNewFile returns false, so you don't have to use the exists method before you call createNewFile.
Tip |
When you create a file with the createNewFile method, the file doesn't have anything in it. If you actually want the file to contain data, you can use the classes I describe in the next chapter to write information to the file. |
Getting information about a file
Several of the methods of the File class simply return information about a file or directory. For example, you can find out if the File object represents a file or directory by calling its isDirectory or isFile method. Other methods let you find out if a file is read-only or hidden, or retrieve the file's age and when it was last modified.
You can get the name of the file represented by a File object in several popular ways:
- To get just the filename, use the getName method. This method returns a string that includes just the filename, not the complete path.
- To get the path that was specified to create the File object (such as logshit.log), use the toString method instead.
- To get the full path for a file-that is, the complete path including the drive letter (for Windows systems) and all the directories and subdirectories leading to the file-use the getCannonicalPath method. This method removes any system-dependent oddities such as relative paths, dots (which represent the current directory), and double-dots (which represent the parent directory) to get the file's actual path.
Getting the contents of a directory
A directory is a file that contains a list of other files or directories. Because a directory is just a special type of file, it is represented by an object of the File class. You can tell if a particular File object is a directory by calling its isDirectory method. If this method returns true, the File is a directory. You can then get an array of all the files contained in the directory by calling the listFiles method.
For example, the following code snippet lists the name of every file in a directory whose pathname is stored in the String variable path:
File dir = new File(path); if (dir.isDirectory()) { File[] files = dir.listFiles(); for (File f : files) System.out.println(f.getName()); }
The following snippet is a little more selective: It lists only files, not subdirectories, and it doesn't list hidden files:
File dir = new File(path); if (dir.isDirectory()) { File[] files = dir.listFiles(); for (File f : files) { if (f.isFile() && !f.isHidden()) System.out.println(f.getName()); } }
Tip |
Directory listings are especially well suited to recursive programming because each File object returned by the listFiles method may be another directory that itself has a list of files and directories. For an explanation of recursive programming along with an example that lists directories recursively, see Book V, Chapter 4. |
Renaming files
You can rename a file by using the renameTo method. This method uses another File object as a parameter that specifies the file you want to rename the current file to. It returns a boolean value that indicates whether or not the file was successfully renamed.
For example, the following statements change the name of a file named hits.log to savedhits.log:
File f = new File("hits.log"); if (f.renameTo(new File("savedhits.log"))) System.out.println("File renamed."); else System.out.println("File not renamed.");
Depending on the capabilities of the operating system, the renameTo method can also move a file from one directory to another. For example, this code moves the file hits.log from the folder logs to the folder savedlogs:
File f = new File("logs\hits.log"); if (f.renameTo(new File("savedlogs\hits.log"))) System.out.println("File moved."); else System.out.println("File not moved.");
Tip |
Always test the return value of the renameTo method to make sure the file was successfully renamed. |
Deleting a file
To delete a file, create a File object for the file, and then call the delete method. For example:
File f = new File("hits.log"); if (f.delete()) System.out.println("File deleted."); else System.out.println("File not deleted.");
If the file is a directory, the directory must be empty to be deleted.
Tip |
With some recursive programming, you can create a method that deletes a non-empty directory. The method looks something like this: private static void deleteFile(File dir) { File[] files = dir.listFiles(); for (File f : files) { if (f.isDirectory()) deleteFile(f); else f.delete(); } dir.delete(); } |
Then, to delete a folder named folder1 along with all its files and subdirectories, call the deleteFile method:
deleteFile(new File("folder1");
Warning |
This feature is extremely dangerous to add to a program! Don't use it without first carefully testing it. If you accidentally delete all the files on your hard drive, don't blame me. |
Using Command Line Parameters
Ever since Book I, Chapter 1, I've used this construction in every Java program presented so far:
public static void main(String[] args)
It's high time you find out what the args parameter of the main method is used for. The args parameter is an array of strings that lets you access any command-line parameters that are specified by the user when he or she runs your program.
For example, suppose you run a Java program named Test from a command program like this:
C:>java Test the quick brown fox
In this case, the Java program is passed four parameters: the, quick, brown, and fox. You can access these parameters via the args array.
Suppose the main method of the Test class is written like this:
public static void main(String[] args) { for (String s : args) System.out.println(s); }
Then the program displays the following output on the console when run with the command shown a few paragraphs back:
the quick brown fox
Command-line parameters are useful in Java programs that work with files as a way to pass pathnames to the program. For example, here's a program that lists all the files in a directory passed to the program as a parameter:
import java.io.*; public class ListDirectory { public static void main(String[] args) { String path = args[0]; File dir = new File(path); if (dir.isDirectory()) { File[] files = dir.listFiles(); for (File f : files) { System.out.println(f.getName()); } } else System.out.println("Not a directory."); } }
Choosing Files in a Swing Application
For the most part, you don't want to mess around with command-line parameters in Swing applications. Instead, you want to use the JFileChooser class to let users pick the files they want to work with. This class lets you display Open and Save dialog boxes similar to the ones you've seen in other GUI applications with just a few lines of code.
For example, Figure 1-1 shows an Open dialog box created with just these two lines of code:
JFileChooser fc = new JFileChooser(); int result = fc.showOpenDialog(this);
Figure 1-1: An Open dialog box displayed by the JFileChooser class.
This code appears in a frame class that extends the JFrame class, so the this keyword in the showOpenDialog call refers to the parent frame.
The result returned by the showOpenDialog method indicates whether the user chose to open a file or click Cancel. And the JFileChooser class provides a handy getSelectedFile method you can use to get a File object for the file selected by the user.
REMEMBER |
The important thing to remember about the JFileChooser class is that it doesn't actually open or save the file selected by the user. Instead, it just returns a File object for the file the user selects. Your program then has the task of opening or saving the file. |
Of course, the JFileChooser class has many additional methods you can use to tailor its appearance and behavior just about any way imaginable. Table 1-2 lists the commonly used constructors and methods of this powerful class.
Table 1-2: The JFileChooser Class
Constructor |
Description |
---|---|
JFileChooser() |
Creates a file chooser that begins at the user's default directory. On Windows systems, this is usually My Documents. |
JFileChooser(File file) |
Creates a file chooser that begins at the location indicated by the file parameter. |
JFileChooser(String path) |
Creates a file chooser that begins at the location indicated by the path string. |
Method |
Description |
---|---|
void addChoosableFileFilter (FileFilter filter) |
Adds a file filter to the chooser. |
File getSelectedFile() |
Returns a File object for the file selected by the user. |
File[] getSelectedFiles() |
Returns an array of File objects for the files selected by the user if the file chooser allows multiple selections. |
void setAcceptAllFileFilterUsed (boolean value) |
If false, removes the All Files filter from the file chooser. |
void setApproveButtonText (String text) |
Sets the text for the Approve button. |
void setDialogTitle (String title) |
Sets the title displayed by the file-chooser dialog box. |
void setFileHidingEnabled (boolean value) |
If true, hidden files are not shown. |
void setMultiSelectionEnabled (boolean value) |
If true, the user can select more than one file. |
int showDialog(Component parent, String text) |
Displays a custom dialog with the specified text for the Accept button. The return values are JFileChooser. CANCEL_OPTION, APPROVE_ OPTION, and ERROR_OPTION. |
void setFileSelectionMode (int mode) |
Determines whether the user can select files, directories, or both. The parameter can be specified as JFileChooser. FILES_ONLY, DIRECTORIES_ONLY, or FILES_AND_DIRECTORIES. |
int showOpenDialog (Component parent) |
Displays an Open dialog box. The return values are the same as for the showDialog method. |
int showSaveDialog (Component parent) |
Displays a Save dialog box. The return values are the same as for the showDialog method. |
Creating an Open dialog box
As you've just seen, you can create an Open dialog box with just a few lines of code. First, you call the JFileChooser constructor to create a JFileChooser instance. Then you call the showOpenDialog method to display an Open dialog box.
If you don't pass a parameter to the constructor, the file chooser starts in the user's default directory, which on most systems is the operating system's current directory. If you want to start in some other directory, you have two options:
- You can create a File object for the directory, and then pass the File object to the constructor.
- You can just pass the pathname for the directory you want to start in to the constructor.
The JFileChooser class also includes methods that let you control the appearance of the chooser dialog box. For example, you can use the setDialogTitle method to set the title (the default is Open), and you can use the setFileHidingEnabled method to control whether or not hidden files are shown. If you want to allow the user to select more than one file, use the setMultiSelectionEnabled method.
A setFileSelectionMode method lets you specify whether users can select files, directories, or both. The options for this method need a little explanation:
- JFileChooser.FILES_ONLY: With this option, which is the default, the user can only choose files with the file chooser dialog box. The user can navigate through directories in the file chooser dialog box, but can't actually select a directory.
- JFileChooser.DIRECTORIES_ONLY: With this option, the user can select only directories, not files. One common use for this option is to let the user choose a default location for files used by your application without actually opening a file.
- JFileChooser.FILES_AND_DIRECTORIES: This option lets the user select either a file or a directory. For most applications, you want the user to pick either a file or a directory, but not both. So you probably won't use this option much.
Tip |
In addition to an Open dialog box, you can also display a Save dialog box by calling the showSaveDialog method. A Save dialog box is similar to an Open dialog box, but has different default values for the title and the text shown on the Approve button. Otherwise, these dialog boxes work pretty much the same. |
Getting the selected file
The file chooser dialog box is a modal dialog box, which means that after you call the showOpenDialog method, your application is tied up until the user closes the file chooser dialog box by clicking the Open or Cancel button.
You can find out which button the user clicked by inspecting the value returned by the showOpenDialog method:
- If the user clicked Open, this value is
JFileChooser.APPROVE_OPTION.
- If the user clicked Cancel, the return value is
JFileChooser.CANCEL_OPTION.
- If an I/O or other error occurred, the return value is
JFileChooser.ERROR_OPTION.
Assuming the showOpenDialog method returns APPROVE_OPTION, you can then use the getSelectedFile method to get a File object for the file selected by the user. Then you can use this File object elsewhere in the program to read or write data.
Putting it all together, then, here's a method that displays a file chooser dialog box and returns a File object for the file selected by the user. If the user cancels or an error occurs, null is returned:
private File getFile() { JFileChooser fc = new JFileChooser(); int result = fc.showOpenDialog(null); File file = null; if (result == JFileChooser.APPROVE_OPTION) file = fc.getSelectedFile(); return file; }
You can call this method from an action event handler when the user clicks a button, selects a menu command, or otherwise indicates that he or she wants to open a file.
Using file filters
The file chooser dialog box includes a Files of Type drop-down list filter that the user can choose to control what types of files are displayed by the chooser. By default, the only item available in this drop-down list is All Files, which doesn't filter the files at all. If you want to add another filter to this list, you must first create a class that extends the FileFilter abstract class. Then you pass an instance of this class to the addChoosableFileFilter method.
Table 1-3 lists the methods of the FileFilter class. Fortunately, it has only two methods you need to implement. This class is in the javax.swing. filechooser package.
Method |
Description |
---|---|
public boolean abstract accept(File f) |
You must implement this method to return true if you want the file displayed in the chooser, false if you don't want the file displayed. |
public String abstract getDescription() |
You must implement this method to return the description string that is displayed in the Files of Type drop-down list in the chooser dialog box. |
TECHNICAL STAUFF |
For some reason, the Java designers gave this class the same name as an interface that's in the java.io package, which is also frequently used in applications that work with files. As a result, you need to fully qualify this class when you extend it, like this: class JavaFilter extends javax.swing.filechooser.FileFilter |
The getDescription method simply returns the text displayed in the Files of Type drop-down list. You usually implement it with a single return statement that returns the description. Here's an example:
public String getDescription() { return "Java files (*.java)"; }
Here the string Java files (*.java) is displayed in the Files of Type drop-down list.
The accept method does the work of a file filter. The file chooser calls this method for every file it displays. The file is passed as a parameter. The accept method returns a boolean that indicates whether or not the file is displayed.
The accept method can use any criteria it wants to decide which files to accept and which files to reject. Most filters do it based on the file extension part of the filename. Unfortunately, the File class doesn't have a method that returns the file extension. But you can get the name with the getName method, and then use the matches method with a regular expression to determine if the file is of the type you're looking for. For example, here's an if statement that determines whether the filename in the name variable is a .java file:
if (name.matches(".*\.java"))
Here the regular expression matches strings that begin with any sequence of characters and end with .java. (For more information about regular expressions, refer to Book V, Chapter 3.)
Here's a complete file filter class that displays files with the extension .java:
private class javaFilter extends javax.swing.filechooser.FileFilter { public boolean accept(File f) { if (f.isDirectory()) return true; String name = f.getName(); if (name.matches(".*\.java")) return true; else return false; } public String getDescription() { return "Java files (*.java)"; } }
After you create a class that implements a file filter, you can add the file filter to the file chooser by calling the addChoosableFileFilter method, passing a new instance of the file filter class:
fc.setChoosableFileFilter(new JavaFilter());
If you want, you can remove the All Files filter by calling the method setAcceptAllFileFilterUsed, like this:
fc.setAcceptAllFileFilterUsed(false);
Then only file filters you add to the file chooser appear in the Files of Type drop-down list.