Создание канонического пути
Кто-нибудь знает какие-либо библиотеки Java, которые я мог бы использовать для генерации канонических путей (в основном удалять обратные ссылки).
Мне нужно что-то, что сделает следующее:
Исходный путь → Канонический путь
/../foo/ -> /foo
/foo/ -> /foo
/../../../ -> /
/./foo/./ -> /foo
//foo//bar -> /foo/bar
//foo/../bar -> /bar
и т.д...
В настоящее время я лениво полагаюсь на использование:
new File("/", path).getCanonicalPath();
Но это устраняет путь к фактической файловой системе и синхронизируется.
java.lang.Thread.State: BLOCKED (on object monitor)
at java.io.ExpiringCache.get(ExpiringCache.java:55)
- waiting to lock <0x93a0d180> (a java.io.ExpiringCache)
at java.io.UnixFileSystem.canonicalize(UnixFileSystem.java:137)
at java.io.File.getCanonicalPath(File.java:559)
Пути, которые я canonicalising не существует в моей файловой системе, так что логика метода будет делать меня хорошо, поэтому не требуется никакой синхронизации. Я надеюсь на хорошо протестированную библиотеку, вместо того, чтобы писать самостоятельно.
Ответы
Ответ 1
Я думаю, что вы можете использовать класс URI для этого; например, если путь не содержит символов, которые необходимо экранировать в компоненте пути URI, вы можете сделать это.
String normalized = new URI(path).normalize().getPath();
Если путь содержит (или может содержать) символы, которые необходимо экранировать, конструкторы с несколькими аргументами будут экранироваться от аргумента path
, а для других аргументов вы можете указать значение null
.
Заметки:
-
Вышеуказанное нормализует путь к файлу, обрабатывая его как относительный URI. Если вы хотите нормализовать весь URI... включая (необязательную) схему, полномочия и другие компоненты, не вызывайте getPath()
!
-
Нормализация URI не требует рассмотрения файловой системы, как это делает канонизация файлов. Но с другой стороны, нормализация ведет себя иначе, чем канонизация, когда в пути есть символические ссылки.
Ответ 2
Использование Apache Commons IO (хорошо известная и хорошо протестированная библиотека)
public static String normalize(String filename)
сделает именно то, что вы ищете.
Пример:
String result = FilenameUtils.normalize(myFile.getAbsolutePath());
Ответ 3
Если вам не нужна канонизация пути, а только нормализация, в Java 7 вы можете использовать метод java.nio.file.Path.normalize
.
Согласно http://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html:
Этот метод не имеет доступа к файловой системе; путь может не найти файл, который существует.
Если вы работаете с файлом File, вы можете использовать что-то вроде этого:
file.toPath().normalize().toFile()
Ответ 4
Вы можете попробовать такой алгоритм:
String collapsePath(String path) {
/* Split into directory parts */
String[] directories = path.split("/");
String[] newDirectories = new String[directories.length];
int i, j = 0;
for (i=0; i<directories.length; i++) {
/* Ignore the previous directory if it is a double dot */
if (directories[i].equals("..") && j > 0)
newDirectories[j--] = "";
/* Completely ignore single dots */
else if (! directories[i].equals("."))
newDirectories[j++] = directories[i];
}
/* Ah, what I would give for String.join() */
String newPath = new String();
for (i=0; i < j; i++)
newPath = newPath + "/" + newDirectories[i];
return newPath;
}
Это не идеально; он линейный по числу каталогов, но делает копию в памяти.
Ответ 5
Какой вид пути квалифицирован как канонический путь, зависит от ОС.
Поэтому Java необходимо проверить на файловой системе.
Таким образом, нет простой логики для проверки пути, не зная ОС.
Ответ 6
Я предполагаю, что у вас есть строки и вам нужны строки, и теперь у вас есть Java 7, и ваша файловая система по умолчанию использует "/" в качестве разделителя путей, поэтому попробуйте:
String output = FileSystems.getDefault().getPath(input).normalize().toString();
Вы можете попробовать это:
/**
* Input Output
* /../foo/ -> /foo
* /foo/ -> /foo
* /../../../ -> /
* /./foo/./ -> /foo
* //foo//bar -> /foo/bar
* //foo/../bar -> /bar
*/
@Test
public void testNormalizedPath() throws URISyntaxException, IOException {
String[] in = new String[]{"/../foo/", "/foo/", "/../../../", "/./foo/./",
"//foo/bar", "//foo/../bar", "/", "/foo"};
String[] ex = new String[]{"/foo", "/foo", "/", "/foo", "/foo/bar", "/bar", "/", "/foo"};
FileSystem fs = FileSystems.getDefault();
for (int i = 0; i < in.length; i++) {
assertEquals(ex[i], fs.getPath(in[i]).normalize().toString());
}
}