Java如何在new File()中解析相对路径?

58

我正在尝试理解Java在创建File对象时如何解析相对路径。

使用的操作系统: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: Access is denied.

我的问题是:

  1. 为什么"test/../test.txt"被视为相对路径并在"user.dir"中创建文件,但"test/../../test.txt"返回错误?对于路径"test/../../test.txt",它试图在哪里创建文件?
  2. 当指定的相对路径未找到时,似乎文件被创建在user.dir中。所以,在我看来,下面两种情况做的事情是一样的:

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

那么是否存在现实世界中的情况,人们会使用方案1而不是方案2呢?

我想我可能错过了一些显而易见的东西,或者基本上误解了相对路径。我查看了File的Java文档,但是找不到解释。Stack Overflow上有很多关于相对路径的问题,但我查找的那些都是针对特定情况而非关于如何解析相对路径的确切讨论。

如果有人能够解释这个问题或指向相关链接,那就太好了。


请注意,user.dir是一个相当不稳定的变量位置,应用程序依赖它可能会很脆弱。另一个需要考虑的因素是,应用程序可能没有安装在具有写入权限的位置。更优化和健壮的方法是在user.home(子目录中)创建文件。请参见此答案以获取简短示例。 - Andrew Thompson
注意:test/test.txttest/../test.txt都是相对路径。在这个意义上,相对路径与嵌入的..组件无关。 - Boann
7个回答

32

有一个概念叫做工作目录
这个目录用一个.(点)表示。
在相对路径中,除了它以外的一切都是相对于它的。

简单来说,.(工作目录)是你运行程序的地方。
在某些情况下,工作目录可以被改变,但通常这就是点所代表的位置。我认为在你的情况下,这应该是C:\JavaForTesters\

所以,test\..\test.txt的意思是:在我的工作目录中,进入子目录test,然后向上一级,最后找到文件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


15
当您的路径从根目录开始,例如在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)来比较文件,这意味着两个相同的文件的File对象可能不相等,因此File在像MapSet这样的集合中使用是不安全的。


6
工作目录是几乎所有操作系统和编程语言等的共同概念。它是您的程序运行的目录。这通常(但并不总是,有改变的方法)是应用程序所在的目录。
相对路径是不带驱动器说明符开头的路径。在Linux中,它们不以/开头,在Windows中,它们不以C:\等开头。这些路径始终从您的工作目录开始。
绝对路径是以驱动器(或网络路径的机器)说明符开头的路径。它们始终从该驱动器的开头开始。

5
相对路径最好是在了解Java运行程序的方式后理解。当运行Java程序时,有一个工作目录的概念。假设你有一个名为FileHelper的类,在/User/home/Desktop/projectRoot/src/topLevelPackage/下进行IO操作。
根据你调用java来运行程序的情况,你会有不同的工作目录。如果你从IDE内部运行程序,它很可能是projectRoot
  • 这种情况下$ projectRoot/src : java topLevelPackage.FileHelper将是src

  • 这种情况下$ projectRoot : java -cp src topLevelPackage.FileHelper将是projectRoot

  • 这种情况下$ /User/home/Desktop : java -cp ./projectRoot/src topLevelPackage.FileHelper将是Desktop

(假设$是标准Unix样式文件系统的命令提示符。类似于Windows系统的对应/平行线)

因此,相对路径根(.)会解析到你的工作目录。为了更好地确定文件写入位置,请考虑以下方法。
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

虽然与问题略有关联,但请尝试理解以下内容。这真是太不直观了:

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  !!
  }
}

2
在Windows和Netbeans中,您可以将相对路径设置为:
    new FileReader("src\\PACKAGE_NAME\\FILENAME");

在Linux和Netbeans中,您可以将相对路径设置为:
    new FileReader("src/PACKAGE_NAME/FILENAME");

如果你的代码在源代码包中,我不知道eclipse或其他IDE是否相同。

1

我参考了peter.petrov的答案,但让我来解释一下如何进行文件编辑以将其更改为相对路径。

只需编辑"AXLAPIService.java"并更改

url = new URL("file:C:users..../schema/current/AXLAPI.wsdl");

url = new URL("file:./schema/current/AXLAPI.wsdl");

或任何您想要存储的位置。

您仍然可以将wsdl文件打包到jar的meta-inf文件夹中,但这是使其在我的情况下工作的最简单方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接