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ランタイム環境(ビルド1.7.0_10-b18)
Java HotSpot(TM)64ビットサーバーVM(ビルド23.6-b04、混合モード)

動作はCに似ているようで、ファイルシステム上のファイルの検証に使用される文字列はnullで切り捨てられます。

File.exists()しかし、これらの無効なファイル名に対してJavaでの動作がfalseを返すことを期待します。

更新:2013年9月19日

Java 1.7.0 update 40は、バグ JDK-8014846の一部としてこれを修正しました:java.ioのファイルおよびその他のクラスは、埋め込まれたnullを適切に処理しません

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が存在します:true
  filename.length = 5
  nio issymlink = false
a.txt(null)が存在します:true
  filename.length = 6
スレッド「メイン」の例外java.nio.file.InvalidPathException:ヌル文字は許可されていません:/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

さて、これは私の3回目の試みです。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)

nativeJavaソースでは、メソッドの実装を使用していることがわかりますgetBooleanAttributes(File f)。これは、JVMがJVMのc /c++ライブラリと相互作用することを意味します。その場合、記号が含まれるすべての文字列は、('\ 0')記号の0x00前の文字列のようにライブラリで解釈されます。0x00

この仮定をどのように確認できますか?簡単な実験を行いました。OSライブラリ内でこの文字列をカットすることについての私の仮定が正しければ、次のコードは次のようになります。

    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)

UPD:

これ:

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

a拡張子なしで名前が付けられたファイルを作成します。

于 2013-01-02T18:40:57.523 に答える
0

これは、基盤となるOSとファイルシステムによってのみ決定されます。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では、ほとんどすべての機能がネイティブであるため、この場合、この動作は基盤となるOS(またはJDK dll)によって決定されます。

于 2013-01-02T19:35:47.937 に答える