Подтвердить что ты не робот

Создание канонического пути

Кто-нибудь знает какие-либо библиотеки 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 не существует в моей файловой системе, так что логика метода будет делать меня хорошо, поэтому не требуется никакой синхронизации. Я надеюсь на хорошо протестированную библиотеку, вместо того, чтобы писать самостоятельно.

4b9b3361

Ответ 1

Я думаю, что вы можете использовать класс URI для этого; например, если путь не содержит символов, которые необходимо экранировать в компоненте пути URI, вы можете сделать это.

String normalized = new URI(path).normalize().getPath();

Если путь содержит (или может содержать) символы, которые необходимо экранировать, конструкторы с несколькими аргументами будут экранироваться от аргумента path, а для других аргументов вы можете указать значение null.

Заметки:

  1. Вышеуказанное нормализует путь к файлу, обрабатывая его как относительный URI. Если вы хотите нормализовать весь URI... включая (необязательную) схему, полномочия и другие компоненты, не вызывайте getPath() !

  2. Нормализация 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());
    }
}