How To Find a File
Fall 2006, David Matuszek

Suppose you want to access (read or write) a file. This should be simple. It isn't.

Suppose, for example, we want to read the text file dummy.txt and print its contents. The following code should work--and just to make matters worse, it often does work--but you cannot depend on it.

FileReader r = new FileReader("dummy.txt");
BufferedReader b = new BufferedReader(r);
String line;

while((line = b.readLine()) != null) {
    System.out.println(line);
}
b.close();
r.close();

The problem is this line:

FileReader r = new FileReader("dummy.txt");

You expect that "dummy.txt" is a relative path name, and it is. You also expect it to be relative to the directory that holds your main class--but it isn't. It's relative to the current directory, which is the directory from which you (or your IDE) started Java. This may or may not be the same as the directory holding your main class.

Letting the user choose the file

This is my usual workaround. Just open a JFileChooser and ask the user to find the file for you. It's simple and it always works.

However, if you want to open the same file every time--maybe it's an initialization file--you don't want to ask the user to open it for you. Most especially, you don't want to give the user directions on which file to open; the user will think (correctly) that if the program knows which file to open, it should just open it.

Absolute path names

One way around this is to use absolute path names. For example:

String dir1 = "C:/Documents and Settings/Matuszek/";
String dir2 = "My Documents/workspace/MyProject";
String fileName = "dummy.txt";
FileReader r = new FileReader(dir1 + dir2 + "/" + fileName);

This is a terrible solution.

Do not use absolute path names!

Correctly interpreting relative paths

As noted above, you would normally want to use paths relative to the directory containing your main class, not the directory from which Java was started. Therefore, you need to know what directory the main class is in. To do this:

  1. Get the Class object for the main class (or any class in the directory you want to start from).
  2. Get the class loader that loaded this class into memory for you.
    1. import java.net.URL;
    2. ClassLoader loader = myClass.getClassLoader();
  3. Tell the class loader to find the file you want (it will search every directory on Java's class path).
  4. Find the path part of the URL.
  5. Spaces are not allowed in a URL, so they are replaced by the three-character sequence %20. That doesn't work in a path; you have to turn each %20 back into a space.
  6. Finally, you can use the path thus found.

Putting this all together, and leaving out the unnecessary variables, we have:

FileReader r = new FileReader(RelativeFile.class.getClassLoader()
                              .getResource("dummy.txt").getPath()
                              .replaceAll("%20", " "));

See? Nothing to it!