2010-04-21 16 views
20

¿Alguien sabe de alguna biblioteca de Java que podría utilizar para generar rutas canónicas (básicamente eliminar referencias anteriores).Generando una ruta canónica

Necesito algo que va a hacer lo siguiente:

Ruta Raw -> itinerario jurídico

/../foo/  -> /foo 
/foo/   -> /foo 
/../../../  ->/
/./foo/./  -> /foo 
//foo//bar  -> /foo/bar 
//foo/../bar -> /bar 

etc ...

En el momento en que perezosamente confiar en el uso:

new File("/", path).getCanonicalPath(); 

Pero esto resuelve el pat h contra el sistema de archivos real, y está sincronizado.

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) 

Los caminos que estoy canonicalising no existen en mi sistema de archivos, por lo que sólo la lógica del método me hará bien, lo que no requiere ninguna sincronización. Estoy esperando una biblioteca bien probada en lugar de tener que escribir la mía.

+0

¿Las rutas relativas son compatibles con la entrada? ¿O es eso una condición de error? –

+0

¿Cuál debería ser la salida para '/ foo /../ bar /'? –

+0

@Joachim: supongo que todas las rutas se basan en la raíz. En la mayoría de los casos, solo elimino referencias de rutas en urls. – Joel

Respuesta

18

Creo que puede usar la clase URI para hacerlo; p.ej. si la ruta de acceso no contiene caracteres que deban escaparse en un componente de ruta de URI, puede hacerlo.

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

Si la ruta contiene (o puede contener) caracteres que requieran códigos de escape, los constructores de múltiples argumento escapar el argumento path, y le puede proporcionar null para los otros argumentos.

Tenga en cuenta que la normalización de URI no implica mirar el sistema de archivos como lo hace la canonicalización de archivos. Pero la otra cara de la moneda es que la normalización se comporta de manera diferente a la canonización cuando hay enlaces simbólicos en la ruta.

+0

Se ve bien. Todavía requiere un pequeño ajuste (para eliminar los principales /.../ pero me lleva la mayor parte del camino, gracias. – Joel

+1

@Joel: ¿Por qué quieres eliminar el '/../'? O bien están equivocados y debe tratarlos como una condición de error o especificar que todas las rutas sean relativas a algún punto y debe soportarlas. Pero eliminarlas silenciosamente suena como una mala idea. –

+0

Probablemente tenga razón, pero obtengo todo tipo de datos desagradables. y simplemente lo estoy limpiando asegurando que todas las rutas estén enraizadas en/ – Joel

4

Usted podría tratar de un algoritmo como esto:

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; 
} 

No es perfecto; es lineal sobre la cantidad de directorios pero hace una copia en la memoria.

14

Usando Apache Commons IO (una biblioteca bien conocido y probado-)

public static String normalize(String filename) 

hará exactamente lo que estás buscando.

Ejemplo:

String result = FilenameUtils.normalize(myFile.getAbsolutePath()); 
0

¿Qué tipo de ruta está calificada como un itinerario jurídico depende del sistema operativo. Es por eso que Java necesita verificarlo en el sistema de archivos. Así que no hay una lógica simple para probar la ruta sin conocer el sistema operativo.

9

Si no necesita la canonización de ruta pero solo la normalización, en Java 7 puede usar el método java.nio.file.Path.normalize. Según http://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html:

Este método no tiene acceso al sistema de archivos; la ruta no puede ubicar un archivo que existe.

Si trabaja con objeto de archivo que puede usar algo como esto:

file.toPath().normalize().toFile() 
-2

Estoy suponiendo que tiene cuerdas y quiere cuerdas, y tiene Java 7 disponibles ahora, y su archivo por defecto sistema utiliza '/' como separador de ruta, a fin de tratar:

String output = FileSystems.getDefault().getPath(input).normalize().toString(); 

puede probar esto con:

/** 
* 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()); 
    } 
}