12

当在 中的文件名中遇到 null 时java.io.File,该字符及其后面的所有字符都将被忽略,从而导致File.exists().

java.io.File.exists()这种行为是我错过的某些方面吗?

例子:

package os;

import java.io.File;
import java.io.IOException;

public class FileNullCheck
{
    public static void main(String[] args)
    {
        File tmp = new File("a.txt");
        try
        {
            tmp.createNewFile();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return;
        }

        String a = "a.txt";
        System.out.printf("a.txt exists: %b (len=%d)%n",new File(a).exists(),a.length());

        String anull = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00 });
        System.out.printf("a.txt (null) exists: %b (len=%d)%n",new File(anull).exists(),anull.length());

        String anullx = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00, 'x' });
        System.out.printf("a.txt (nullx) exists: %b (len=%d)%n",new File(anullx).exists(),anullx.length());
    }
}

运行这个的结果。

a.txt 存在:true (len=5)
a.txt (null) 存在:true (len=6)
a.txt (nullx) 存在:true (len=7)

Linux系统有以下JVM。

Java(TM) SE 运行时环境 (build 1.7.0_10-b18)
Java HotSpot(TM) 64 位服务器 VM(内部版本 23.6-b04,混合模式)

该行为似乎类似于 C,并且用于验证文件系统上的文件的字符串在 null 处被截断。

但我希望 Java 的行为File.exists()对这些无效的文件名返回 false。

更新:2013 年 9 月 19 日

Java 1.7.0 更新 40 已将此作为错误 JDK-8014846 的一部分进行了修复:java.io 中的文件和其他类无法正确处理嵌入的空值

4

4 回答 4

3

在 RHEL 上,nul 字节似乎终止了文件名(正如您在 C 中所期望的那样)

System.out.println("a exists " + new File("a").exists());
FileOutputStream fos = new FileOutputStream(new File("a\u0000aa"));
fos.close();
System.out.println("a exists " + new File("a").exists());

印刷

a exists false
a exists true

我怀疑 Java 应该阻止您尝试使用带有 nul 字节的文件名。

于 2013-01-02T18:25:55.020 回答
1

如果使用 JDK 1.7+,则 java.nio.files.Paths.get(URI) 可用于测试 Nul(似乎)

对原始测试的修改会产生有用的异常

package os;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileNullCheck
{
    public static void main(String[] args) throws Exception
    {
        File tmp = new File("a.txt");
        try
        {
            tmp.createNewFile();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return;
        }

        String a = "a.txt";
        testExists("a.txt", a);

        String anull = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00 }, "UTF-8");
        testExists("a.txt (null)", anull);

        String anullx = new String(new byte[] { 'a', '.', 't', 'x', 't', 0x00, 'x' }, "UTF-8");
        testExists("a.txt (nullx)", anullx);
    }

    private static void testExists(String label, String filename) throws IOException
    {
        File file = new File(filename);
        System.out.printf("%s exists: %b%n", label, file.exists());
        System.out.printf("  filename.length = %d%n", filename.length());
        Path path = Paths.get(file.toURI());
        boolean symlink = Files.isSymbolicLink(path);
        System.out.printf("  nio issymlink = %b%n",symlink);
    }
}

输出结果

a.txt 存在:真
  文件名.长度 = 5
  nio issymlink = false
a.txt (null) 存在:真
  文件名.长度 = 6
线程“主”java.nio.file.InvalidPathException 中的异常:不允许 Nul 字符:/home/joakim/code/Stackoverflow/a.txt
    在 sun.nio.fs.UnixPath.checkNotNul(UnixPath.java:93)
    在 sun.nio.fs.UnixPath.normalizeAndCheck(UnixPath.java:83)
    在 sun.nio.fs.UnixPath.(UnixPath.java:71)
    在 sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:281)
    在 java.io.File.toPath(File.java:2069)
    在 sun.nio.fs.UnixUriUtils.fromUri(UnixUriUtils.java:61)
    在 sun.nio.fs.UnixFileSystemProvider.getPath(UnixFileSystemProvider.java:97)
    在 java.nio.file.Paths.get(Paths.java:138)
    在 os.FileNullCheck.testExists(FileNullCheck.java:39)
    在 os.FileNullCheck.main(FileNullCheck.java:28)

于 2013-01-02T23:21:06.193 回答
0

它仅由底层操作系统和文件系统决定。当您File使用包含零的名称创建时,此文件名在构造期间不会更改File

String anull = new String(new byte[] {'a', 0x00, '.', 't', 'x', 't', 0x00});
System.out.println(anull);
System.out.println(new File(anull).getPath());

输出是相同的并且包含零。

处理文件时File,它使用文件系统(例如java.io.FileSystem),该文件系统的实现取决于 OS 和 JDK(它是内部类)。在 Windows JDK 中,几乎所有函数都是原生的,所以在这种情况下,这种行为是由底层操作系统(或 JDK dll)决定的。

于 2013-01-02T19:35:47.937 回答
0

好吧,这是我的第三次尝试。我在 Windows(Win 7,JDK 7 64 位)下检查了您的代码。它导致相同的结果:

a.txt exists: true (len=5)
a.txt (null) exists: true (len=6)
a.txt (nullx) exists: true (len=7)

在java源代码中我们可以看到,它使用方法native的实现getBooleanAttributes(File f)。这意味着 JVM 与 JVM 的c/c++库进行交互。在这种情况下,所有带有0x00符号的字符串都将在库中被解释为仅在0x00('\0') 符号之前的字符串。

如何检查这个假设?我做了一个简单的实验。如果我关于在操作系统库中剪切这个字符串的假设是正确的,那么这段代码:

    String anull = new String(new byte[] { 'a',0x00 , '.', 't', 'x', 't', 0x00 });
    System.out.printf("a.txt (null) exists: %b (len=%d)%n",new File(anull).exists(),anull.length());

将返回false。是的,就是这样:

a.txt (null) exists: false (len=7)

升级版:

这个:

String anull = new String(new byte[] { 'a',0x00 , '.', 't', 'x', 't', 0x00 });
new File(anull).createNewFile();

将创建一个a没有任何扩展名的文件。

于 2013-01-02T18:40:57.523 回答