Open file in native directory

A directory containing Java .class files can also contain any other kind of file as well. If a text file or a binary file is only used by a single package, then the most natural home for it is often the very same directory/package that uses it. This will tend to increase modularization and coherence. (See the package-by-feature topic as well.)

Such files might be used for:

At runtime, the most convenient way of accessing such files is with these methods:

These methods use the current classloader in order to find files, so they have the most flexibility. The following example reads in a text file placed in the same directory as the running class. Note that the code references the file in the simplest possible way, using its simple file name, without any qualification. As long as the text file retains the same name, and is in the same directory as the class, this code will work.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Scanner;

/** Read a text file that's in the same directory as this (.class) file. */
public final class ReadNeighbouringTextFile {

  public static void main(String... aArgs) throws IOException {
    log("Classpath: " + System.getProperty("java.class.path"));
    log("user.dir: " + System.getProperty("user.dir"));
    ReadNeighbouringTextFile read = new ReadNeighbouringTextFile();
    read.readFileViaStream();
    read.readFileasViaUrl();
    //read.readFileViaPath();
  }

  void readFileViaStream() throws IOException {
    log("Via stream...");
    try (
      //uses the class loader search mechanism:
      InputStream input = this.getClass().getResourceAsStream("test.txt");
      InputStreamReader isr = new InputStreamReader(input, ENCODING);
      BufferedReader reader = new BufferedReader(isr);
    ){
      String line = null;
      while ((line = reader.readLine()) != null) {
        //process the line in some way
        log(line);
      }      
    }
  }
  
  void readFileasViaUrl() throws IOException{
    log("Via URL...");
    //this doesn't work in a Java Web Start context
    //the URL points back to the server; it's not local
    //uses the class loader search mechanism:
    URL url = this.getClass().getResource("test.txt");
    URI uri = null;
    try {
      uri = url.toURI();
    }
    catch(URISyntaxException ex){
      //in practice this will be very rare
      ex.printStackTrace();
    }
    Path path = Paths.get(uri);    
    
    //now that you have the path, it's just regular text file processing
    
    //this gets the whole content at once:
    List<String> lines = Files.readAllLines(path, ENCODING);
    log("Number of lines in the file: "  + lines.size());
    
    //OR, use this style, to process each line one at a time
    try (Scanner scanner =  new Scanner(path, ENCODING.name())){
      while (scanner.hasNextLine()){
        //process each line in some way
        log(scanner.nextLine());
      }      
    }    
  }
  
  /**
   Here, relative Path objects don't know about the file system in the same way that a 
   classloader does. It only knows about the 'user.dir' directory, the base 
   directory of the runtime; this style is much less flexible, and is not 
   recommended.
  */
  void readFileViaPath() throws IOException{
    log("Via path (not recommended)...");
    //Succeeds: absolute reference
    //Path path = Paths.get("C:\\myproj\\test-api\\bin\\test.txt"); 
    
    /*
    * Relative reference.
    * 
    * Fails when the file is beside the .class file.
    * Succeeds only when the test.txt file is in the 'user.dir' directory.
    * 
    * This means that relative paths don't use the classpath; they 
    * only use the 'user.dir' System property.
    */
    Path path = Paths.get("test.txt"); 
    
    log("Path: " + path);
    log("Absolute: " + path.isAbsolute());
    List<String> lines = Files.readAllLines(path, ENCODING);
    log("Number of lines in the file: "  + lines.size());
  }
  
  private final static Charset ENCODING = StandardCharsets.UTF_8;
  
  private static void log(Object aMsg){
    System.out.println(String.valueOf(aMsg));
  }
} 


See Also :
Reading and writing text files
Package by feature, not layer
Would you use this technique?
Yes   No   Undecided   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -