web-dev-qa-db-ja.com

Javaは新しいFile()の相対パスをどのように解決しますか?

JavaがFileオブジェクトの作成中に相対パスを解決する方法を理解しようとしています。

使用OS:Windows

以下のスニペットでは、パスが見つからないためIOExceptionを取得しています。

@Test
public void testPathConversion() {
        File f = new File("test/test.txt");
        try {
            f.createNewFile();
            System.out.println(f.getPath());
            System.out.println(f.getAbsolutePath());    
            System.out.println(f.getCanonicalPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
}

ここでの私の理解は、Javaは提供されたパスを絶対パスとして扱い、パスが存在しない場合にエラーを返すということです。理にかなっています。

上記のコードを更新して相対パスを使用する場合:

@Test
    public void testPathConversion() {
        File f = new File("test/../test.txt");
        try {
            f.createNewFile();
            System.out.println(f.getPath());
            System.out.println(f.getAbsolutePath());    
            System.out.println(f.getCanonicalPath());
        } catch (Exception e) {
            e.printStackTrace();
        }    
    }

新しいファイルを作成し、以下の出力を提供します。

test\..\test.txt
C:\JavaForTesters\test\..\test.txt
C:\JavaForTesters\test.txt

この場合、パスには「/../」が含まれているため、提供されたパスが存在しない場合でも、Javaはこれを相対パスとして扱い、user.dirにファイルを作成します。これも理にかなっています。

しかし、次のように相対パスを更新すると:

   @Test
    public void testPathConversion() {
        File f = new File("test/../../test.txt");
        try {
            f.createNewFile();
            System.out.println(f.getPath());
            System.out.println(f.getAbsolutePath());
            System.out.println(f.getCanonicalPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

次にIOExceptionが発生します:アクセスが拒否されました。

私の質問は:

  1. なぜ"test/../test.txt"が相対パスとして扱われ、"user.dir"にファイルを作成しますが、"test/../../test.txt"はエラーを返しますか?パス"test/../../test.txt"のファイルはどこで作成しようとしますか?
  2. 指定した相対パスが見つからない場合、ファイルはuser.dirに作成されているようです。したがって、以下の2つのシナリオは同じことをしているように見えます。

    //scenario 1
    File f = new File("test/../test.txt");
    f.createNewFile();
    
    //scenario 2
    File f = new File("test.txt");
    f.createNewFile();
    

それでは、シナリオ2の代わりにシナリオ1を使用する現実のケースはありますか?

私はここで明白な何かを見逃している、または基本的に相対パスを誤解していると思います。 FileのJavaドキュメントを調べましたが、これについての説明が見つかりません。 Stack Overflowには相対パスに関してかなりの数の質問が投稿されていますが、調べたのは特定のシナリオに関するものであり、相対パスの解決方法に関するものではありません。

誰かが私にこれがどのように機能するかを説明したり、いくつかの関連リンクを指摘してくれたら素晴らしいでしょうか?

39
Eswar

working directoryの概念があります。
このディレクトリは、.(ドット)で表されます。
相対パスでは、他のすべてはそれに関連しています。

.(作業ディレクトリ)を置くだけで、プログラムを実行できます。
作業ディレクトリを変更できる場合がありますが、一般的には
ドットが表すもの。あなたの場合、これはC:\JavaForTesters\だと思います。

test\..\test.txtは次を意味します:サブディレクトリtest
作業ディレクトリで、1つ上のレベルに移動してから、
ファイルtest.txt。これは基本的にtest.txtと同じです。

詳細については、こちらをご覧ください。

http://docs.Oracle.com/javase/7/docs/api/Java/io/File.html

http://docs.Oracle.com/javase/tutorial/essential/io/pathOps.html

20
peter.petrov

パスがルートディレクトリ、つまりWindowsのC:\またはUnixまたはJavaリソースパスの/で始まる場合、それは絶対パスと見なされます。それ以外はすべて相対的なので、

new File("test.txt") is the same as new File("./test.txt")

new File("test/../test.txt") is the same as new File("./test/../test.txt")

getAbsolutePathgetCanonicalPathの主な違いは、最初のパスが親と子のパスを連結するため、ドットが含まれる場合があることです:..または.getCanonicalPathは、特定のファイルに対して常に同じパスを返します。

注:File.equalsは、パスの抽象的な形式(getAbsolutePath)を使用してファイルを比較するため、同じ2つのFileオブジェクトが等しくなく、Filesが等しい場合があります。 MapSetなどのコレクションで使用するのは安全ではありません。

9
Andrey Chaschev

作業ディレクトリは、実質的にすべてのオペレーティングシステムとプログラム言語などで共通の概念です。プログラムが実行されているディレクトリです。これは通常、アプリケーションが存在するディレクトリです(常にではありませんが、変更する方法があります)。

相対パスは、ドライブ指定子なしで開始するパスです。したがって、Linuxでは/で始まりません。WindowsではC:\で始まりません。これらは常に作業ディレクトリから始まります。

絶対パスは、ドライブ(またはネットワークパスのマシン)指定子で始まるパスです。彼らは常にそのドライブの開始から行きます。

5
Tim B

Javaがプログラムを実行する方法を知っていれば、相対パスを最もよく理解できます。

Javaでプログラムを実行するときの作業ディレクトリの概念があります。たとえば、/User/home/Desktop/projectRoot/src/topLevelPackage/の下でIOを実行するFileHelperというクラスがあると仮定します。

Javaを呼び出してプログラムを実行する場合に応じて、作業ディレクトリが異なります。 IDE内からプログラムを実行する場合、ほとんどの場合projectRootになります。

  • この場合、$ projectRoot/src : Java topLevelPackage.FileHelpersrcになります。

  • この場合、$ projectRoot : Java -cp src topLevelPackage.FileHelperprojectRootになります。

  • この場合、$ /User/home/Desktop : Java -cp ./projectRoot/src topLevelPackage.FileHelperDesktopになります。

(Assuming $ is your command Prompt with standard Unix-like FileSystem. Similar correspondence/parallels with Windows system)

したがって、相対パスルート(.)は作業ディレクトリに解決されます。したがって、ファイルを書き込む場所をより確実にするために、以下のアプローチを検討すると言われています。

package topLevelPackage

import Java.io.File;
import Java.nio.file.Path;
import Java.nio.file.Paths;

public class FileHelper {

    // Not full implementation, just barebone stub for path
    public void createLocalFile() {

        // Explicitly get hold of working directory
        String workingDir = System.getProperty("user.dir");

        Path filePath = Paths.get(workingDir+File.separator+"sampleFile.txt");

        // In case we need specific path, traverse that path, rather using . or .. 
        Path pathToProjectRoot = Paths.get(System.getProperty("user.home"), "Desktop", "projectRoot");

        System.out.println(filePath);
        System.out.println(pathToProjectRoot);

    }
}

お役に立てれば。

2
Ravi Tiwari

WindowsおよびNetbeansでは、相対パスを次のように設定できます。

    new FileReader("src\\PACKAGE_NAME\\FILENAME");

LinuxおよびNetbeansでは、相対パスを次のように設定できます。

    new FileReader("src/PACKAGE_NAME/FILENAME");

Source Packages内にコードがある場合、Eclipseまたは他のIDEで同じかどうかわかりません

1
Ricard Molins

質問に少しだけ関連がありますが、この問題に頭を包むようにしてください。直感的ではない:

import Java.nio.file.*;
class Main {
  public static void main(String[] args) {
    Path p1 = Paths.get("/personal/./photos/./readme.txt");
    Path p2 = Paths.get("/personal/index.html");
    Path p3 = p1.relativize(p2);
    System.out.println(p3); //prints  ../../../../index.html  !!
  }
}
0
djangofan