Нулевые символы в java.io.File действительны для проверки наличия?
Когда в файле 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 exists: true (len=5)
a.txt (null) exists: true (len=6)
a.txt (nullx) exists: true (len=7)
Система Linux имеет следующую JVM.
Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)
Поведение похоже на C-like, а строка, используемая для проверки файла в файловой системе, усекается нулевым значением.
Но я ожидал бы, что поведение Java вернет false для File.exists()
для этих недопустимых имен файлов.
Обновление: 19 сентября 2013 г.
Обновление Java 1.7.0 40 исправило это как часть ошибки
JDK-8014846: Файл и другие классы в java.io не обрабатывают внедренные нули корректно
Ответы
Ответ 1
В 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-байтом.
Ответ 2
Ну, это моя третья попытка. Я проверил ваш код под 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 взаимодействует с c/c++ библиотеками JVM. В этом случае все строки с символом 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)
UPD:
Это:
String anull = new String(new byte[] { 'a',0x00 , '.', 't', 'x', 't', 0x00 });
new File(anull).createNewFile();
создаст файл с именем просто a
без расширения.
Ответ 3
Он определяется только базовой ОС и файловой системой. Когда вы создаете 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
), реализация которой зависит от ОС и JDK (внутренний класс). В Windows JDK почти все функции являются родными, поэтому в этом случае это поведение определяется базовыми операционными системами (или JDK-dll).
Ответ 4
Если использовать 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 exists: true
filename.length = 5
nio issymlink = false
a.txt (null) exists: true
filename.length = 6
Exception in thread "main" java.nio.file.InvalidPathException: Nul character not allowed: /home/joakim/code/Stackoverflow/a.txt
at sun.nio.fs.UnixPath.checkNotNul(UnixPath.java:93)
at sun.nio.fs.UnixPath.normalizeAndCheck(UnixPath.java:83)
at sun.nio.fs.UnixPath.(UnixPath.java:71)
at sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:281)
at java.io.File.toPath(File.java:2069)
at sun.nio.fs.UnixUriUtils.fromUri(UnixUriUtils.java:61)
at sun.nio.fs.UnixFileSystemProvider.getPath(UnixFileSystemProvider.java:97)
at java.nio.file.Paths.get(Paths.java:138)
at os.FileNullCheck.testExists(FileNullCheck.java:39)
at os.FileNullCheck.main(FileNullCheck.java:28)