【问题标题】:Java : File.exists() inconsistencies when setting "user.dir"Java:设置“user.dir”时 File.exists() 不一致
【发布时间】:2011-01-17 12:52:34
【问题描述】:

JRE 6,在 Windows XP 上。

用不同的构造函数实例化两个 File 对象会导致 File.exists() 方法中的结果不一致。

免责声明: 以下代码为抽象代码,并非实际代码。我根本不相信这是一个 File.separator 问题。我首先要求获得早期反应,以防我错过了一个很好理解的问题。 现在看来,重置 user.dir 系统属性是导致此问题的原因之一。下面的代码现在可以按原样重现和使用。您可以复制/粘贴 Java 类并尝试它,它的行为应该与我列出的结果一致。

设置:

创建文件夹架构C:\toto\tmp\sub

从任何不包含tmp/sub 子文件夹架构的文件夹中启动以下类。

代码:

public class TestFileExists {

    public static void main(String[] args) {

        System.setProperty("user.dir", "C:\\toto\\");

        File root = new File("tmp");

        File sub_a = new File(root, "sub");

        File sub_b = new File(root.getAbsolutePath()+"/sub");

        System.out.println("sub_a path ? "+sub_a.getAbsolutePath());
        System.out.println("sub_a exists ? "+sub_a.exists());
        System.out.println("sub_b path ? "+sub_b.getAbsolutePath());
        System.out.println("sub_b exists ? "+sub_b.exists());
        System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath())));
        System.out.println("Obj equals ? "+ (sub_a.equals(sub_b)));

    }

}

结果:

sub_a path ? C:\toto\tmp\sub
sub_a exists ? false
sub_b path ? C:\toto\tmp\sub
sub_b exists ? true
Path equals ? true
Obj equals ? false

我不明白sub_a exists ? false 行,结果与机器之间不一致,也与根初始路径不一致 ant 现在结果与机器之间一致。

现在,如果您从包含tmp/sub 子文件夹架构的文件夹中通过命令行调用 java 重新执行该类(例如,如果您从 D:\ 调用它,具有 D:\tmp\sub),您将得到预期:

sub_a path ? C:\toto\tmp\sub
sub_a exists ? true
sub_b path ? C:\toto\tmp\sub
sub_b exists ? true
Path equals ? true
Obj equals ? false

sub_a 的存在显然是误报,因为它会检查除getAbsolutePath() 所描述的文件夹之外的另一个文件夹的存在。

所以我强烈怀疑File.exists()依赖于实际Java执行路径,文件存在与绝对路径不一致,而exists()使用“user.dir”系统属性以外的其他路径检查文件系统。

知道这个问题可能来自哪里吗?

【问题讨论】:

  • 猜想:中间目录上的一些属性搞砸了,它会被绝对路径遍历。
  • 在带有 JDK 1.6.0_16 和 Java 1.6.0_18 的 Windows XP 上,我明白了,真的。真假。话虽如此,我的目录没有任何空格、特殊字符或拒绝权限。

标签: java filesystems


【解决方案1】:

不支持设置user.dir。它应该被视为只读属性。

例如,Sun Bug Parade 中对Bug 4117557 的评价包含以下文字:

“user.dir”,在jvm启动时初始化,应该作为一个 信息/只读系统属性,尝试通过命令行自定义它 -Duser.dir=xyz 最终会导致实现依赖/未指定的行为。

虽然此文本是关于在命令行上设置它,但通过 setProperty() 设置它很可能同样未定义。

当您无需手动设置user.dir 即可重现问题时,您就发现了真正的问题。

【讨论】:

  • 这不起作用的原因不是因为不支持设置user.dir。这是因为当您使用文件的相对路径时会忽略 user.dir,而 File.getAbsolutePath() 会考虑到它。这意味着同一个文件可以引用两个位置,具体取决于您是否将其转换为绝对文件。解决方案 - 避免使用相对路径。
  • @Roman:对不起,你错了。这只是系统假定user.dir 从未被用户更改这一事实的影响。 getAbsolutePath() 被记录为针对当前用户目录“以系统相关方式解决”。所以getAbsolutePath() 应该总是返回与您直接使用相对路径访问文件时所访问的完全相同的路径。这就是该方法的全部意义所在!
  • 约阿希姆:对不起,我的误会。我认为“不支持设置 user.dir”意味着不支持 System.setProperty("user.dir", "...") 。但是,无论属性如何设置,都会带来麻烦。
【解决方案2】:

将以下行添加到您的测试中:

System.out.println("sub_a = " + sub_a);
System.out.println("sub_b = " + sub_b);

结论:

1) sub_a 是相对路径,sub_b 是绝对路径。

2)exists()不使用绝对路径

3)设置环境变量user.dir不会改变当前用户目录,exists()使用的目录

4) getAbsolutePath 使用user.dir 变量而不是真正的当前用户目录!至少对于 Windows(请参阅 Win32FileSystem.getuserPath)。那就是问题所在! (错误?)

【讨论】:

  • 当然。似乎这不是一个错误,而是 user.dir 的误用,在执行过程中不被认为是可修改的。
【解决方案3】:

File 可以表示一个抽象路径。为tmp 创建一个File 和一个来自tmp 的绝对路径的对象将不等于File 对象,尽管它们的绝对路径是相等的。

我不确定 sub_a 不存在但 sub_b 会存在的情况,但我怀疑这是分隔符问题。我怀疑这与javadoc for File(File,String) 的这句话有关:

每个路径名字符串都被转换为一个抽象路径名,并且子抽象路径名根据父级解析。

我不知道在基于 Unix 的文件系统中,/full/path/to./tmp/sub 会存在但/full/path/to/tmp 不会存在。

如果问题在系统之间是一致的,则可以通过转储更多每个File 对象的状态而不是仅仅打印比较来更清楚地理解。

【讨论】:

  • 我同意你关于非分隔符的观点。设置 user.dir 属性时似乎会发生这种情况。一旦我对执行的上下文有更透彻的了解,我会尽快更新问题。
【解决方案4】:

在这一行:

File sub_b = new File(root.getAbsolutePath()+"/sub");

您应该使用常量File.separator(取决于底层系统)而不是硬编码的正斜杠。

【讨论】:

  • 不,File 构造函数做的第一件事就是规范化表单,所以这应该没有帮助。
  • 不,我在复制结果时出错了。实际上是 sub_a 被检测为不存在。
【解决方案5】:

我认为是目录分隔符,因为 Windows 通常使用反斜杠“\”,而 Linux 使用普通斜杠“/”。

尝试将行改为:

File sub_b = new File(root.getAbsolutePath() + System.getProperty("file.separator") + "sub");

【讨论】:

  • 不,我在复制结果时出错了。它实际上是被检测为不存在的 sub_a。再次抱歉。
【解决方案6】:

这可能是文件分隔符问题。在 Windows 上,标准文件分隔符是反斜杠 (\)。当您创建sub_b 时,您会创建一个包含斜杠和反斜杠的路径名(作为String)。系统可能会对此感到有些困惑。

【讨论】:

  • 不,我在复制结果时出错了。它实际上是被检测为不存在的 sub_a。对不起。问题已编辑。再次抱歉。
【解决方案7】:

这些结果应该是跨机器确定的。让我逐行分解:

System.out.println("sub_a exists ? "+sub_a.exists());

在这里,您要询问该文件是否确实存在于文件系统中。假设文件存在,这应该总是返回相同的结果。

System.out.println("sub_b exists ? "+sub_b.exists());

同样的事情。您正在检查该文件是否确实存在。

System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath())));

在这里您可以看到 AbsolutePath 是否相同。

System.out.println("Obj equals ? "+ (sub_a.equals(sub_b)));

在这里,您正在与 .equals() 进行对象比较,它在后台将调用 FileSystem 类对两个对象的 path 对象进行比较(),而不是它们的 绝对路径。

很可能是文件分隔符错误。尝试在 sub_b 的构造中替换 File.separator,如下所示:

File sub_b = new File(root.getAbsolutePath()+File.separator+"sub");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多