2

I know this question has been asked in a myriad of variations, but today I wish to emphasize one particular scenario when one wishes to read from a .txt file without specifying the absolute path.

Suppose we have the following set up in Eclipse.

projectName/packageName/subPackage/

and we have a class named Read.java inside the subPackage. The class will be attempting to read from the input1.txt.

We also have input1.txt inside the very same subPackage.

If one uses absolute paths, the code inside Read.java will be something of the following (let's assume now that input1.txt is placed on my Desktop for illustration purposes):

    // Create a list to store the list of strings from each line of the input1.txt.
    LinkedList<String> inputStrings = new LinkedList<String>();

    BufferedReader bufferedTextIn = null;

    try {
        String line;

        // Specify the file
        String fileName = "C:" + File.separator 
                            + "Users" + File.separator 
                            + "Kevin" + File.separator 
                            + "Desktop" +  File.separator 
                            + "input1.txt";

        bufferedTextIn = new BufferedReader(new FileReader(fileName));

        while ((line = bufferedTextIn.readLine()) != null) {
            inputStrings.add(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bufferedTextIn != null) {
                bufferedTextIn.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

The problem with the above is the use of absolute paths to my desktop. If I passed the code to my friend, he would need to manually change the path to his desktop. Even if I put input1.txt in my project folder, my friend would still need to manually change the path to make it work.

Note that using File.separator is a good practice because different OS interprets separators a bit differently, but it is still insufficient.

So what do we do instead?

Kara
  • 6,115
  • 16
  • 50
  • 57
Kevin Lee
  • 2,307
  • 26
  • 31
  • That `File.separator`s are one of the ugliest things I have seen, since it is "C:" why do you bother? edit: whoops, didn't read your question at all. You are trying to get rid of absolute paths. But still, "/" works Windows too –  Mar 11 '14 at 14:20
  • Hahaha ok, I agree that it is ugly! I guess File.separator is functionally 'nice' because it is OS independent if I am not wrong? And I think it also works if there are white spaces between the folder names. (: – Kevin Lee Mar 11 '14 at 14:26

2 Answers2

2

Here is my solution.

String fileName = Read.class.getResource("input1.txt").getPath();

System.out.println(fileName);

bufferedTextIn = new BufferedReader(new FileReader(fileName));

Let's recap the scenario. We have our input1.txt file placed in the SAME FOLDER as the Read.java. So, the code above attempts to go to where Read.class exists (which is somewhere in the bin folder in Eclipse), and look for input1.txt. This is the path RELATIVE to where Read.class is located (in this case, it is trivially in the same folder, but you could very well specify another folder relative to where Read.class is located). The print statement lets you know exactly where it is located and is a good practice while debugging.

When you build in Eclipse, the .java files in the src folder would be compiled into .class files and be placed in the bin folder. The neat thing is that input1.txt is ALSO copied over the bin folder (and all the package hierarchies are maintained).

An important thing to note is to use getPath() rather than toString(), because the latter will add some extra text to the front of the path (I only knew that because I printed it out) and thus you get a NULL pointer exception because the fileName was not formatted correctly.

Another important thing to note is that I used Read.class.getResource("input1.txt").getPath(); instead of this.getClass().getResource("input1.txt").getPath(); because the code was called in a static context (in my main method). If you create an object, then feel free to use the latter.

If you're interested in more advanced features, you can check out the link below:

What is the difference between Class.getResource() and ClassLoader.getResource()?

I hope this helps!

Edit: You could use the following to get the directory where Read.class resides in also.

String fileName = Read.class.getResource(".").getPath();

Specifying getResource("..") would go to the parent directory.

String fileName = Read.class.getResource("..").getPath();

The above may be useful if you want to have more control specifying the path (e.g. if you want to create output.txt inside the directory where Read.class resides, use

String fileName = Read.class.getResource(".").getPath() + "output.txt";
Community
  • 1
  • 1
Kevin Lee
  • 2,307
  • 26
  • 31
1

If you knew the file was going to be located in the same folder in each system this program was run on, you could use system variables to ensure any path defined would still work with different users. For windows, I've used:

String user = new com.sun.security.auth.module.NTSystem().getName();

to get a user name. This could then be substituted in your example to be:

String fileName = "C:" + File.separator 
                        + "Users" + File.separator 
                        + user + File.separator 
                        + "Desktop" +  File.separator 
                        + "input1.txt";

I'm not sure how this would work outside of Windows however.

Jack
  • 508
  • 1
  • 9
  • 18