¿Cómo combinar caminos en Java?

350

¿Hay un equivalente de Java para System.IO.Path.Combine() en C # /. NET? ¿O algún código para lograr esto?

Este método estático combina una o más cadenas en una ruta.

Rodrigue
fuente
1
Esta pregunta SO podría ayudar.
Jim Blizard

Respuestas:

407

En lugar de mantener todo basado en cadenas, debe usar una clase que esté diseñada para representar una ruta del sistema de archivos.

Si está utilizando Java 7 o Java 8, debería considerar usarlo java.nio.file.Path; Path.resolvese puede usar para combinar una ruta con otra o con una cadena. La Pathsclase auxiliar también es útil. Por ejemplo:

Path path = Paths.get("foo", "bar", "baz.txt");

Si necesita atender entornos anteriores a Java-7, puede usar java.io.File, de esta manera:

File baseDirectory = new File("foo");
File subDirectory = new File(baseDirectory, "bar");
File fileInDirectory = new File(subDirectory, "baz.txt");

Si lo desea de nuevo como una cadena más tarde, puede llamar getPath(). De hecho, si realmente quisiera imitar Path.Combine, simplemente podría escribir algo como:

public static String combine(String path1, String path2)
{
    File file1 = new File(path1);
    File file2 = new File(file1, path2);
    return file2.getPath();
}
Jon Skeet
fuente
10
Cuidado con los caminos absolutos. La versión .NET volverá path2(ignorando path1) si path2es una ruta absoluta. La versión de Java descartará el líder /o \ y lo tratará como una ruta relativa.
finnw
23
@Matthew: porque un directorio es un archivo. Es un archivo cuyo contenido define los elementos secundarios de ese directorio, su ubicación en el disco, permisos, etc.
Dónal
77
@Hugo: ¿Entonces desperdicia un total de dos objetos ? ¡Impactante! Me parece bastante limpio, para ser honesto ... mantiene la lógica de los nombres de archivos relativos a los que pertenece, en la clase File.
Jon Skeet
1
@modosansreves: Mira File.getCanonicalPath.
Jon Skeet
1
@SargeBorsch: Bueno, C # es solo un lenguaje. Puede crear fácilmente su propio equivalente de Fileen C # si lo desea. (Estoy asumiendo que quiere decir la existencia de Fileun beneficio, que estoy de acuerdo con.)
Jon Skeet
118

En Java 7, debe usar resolve:

Path newPath = path.resolve(childPath);

Si bien la clase de ruta NIO2 puede parecer un poco redundante para File con una API innecesariamente diferente, de hecho es sutilmente más elegante y robusta.

Tenga en cuenta que Paths.get()(como lo sugirió otra persona) no tiene una sobrecarga tomando un Path, y hacer Paths.get(path.toString(), childPath)NO es lo mismo que resolve(). De los Paths.get()documentos :

Tenga en cuenta que si bien este método es muy conveniente, su uso implicará una referencia asumida al FileSystem predeterminado y limitará la utilidad del código de llamada. Por lo tanto, no debe usarse en código de biblioteca destinado a reutilización flexible. Una alternativa más flexible es utilizar una instancia de ruta existente como un ancla, como:

Path dir = ...
Path path = dir.resolve("file");

La función hermana resolvees la excelente relativize:

Path childPath = path.relativize(newPath);
Aleksandr Dubinsky
fuente
17

Sé que ha pasado mucho tiempo desde la respuesta original de Jon, pero tenía un requisito similar al OP.

Al extender la solución de Jon, se me ocurrió lo siguiente, que tomará uno o más segmentos de ruta y tomará tantos segmentos de ruta que pueda lanzarle.

Uso

Path.combine("/Users/beardtwizzle/");
Path.combine("/", "Users", "beardtwizzle");
Path.combine(new String[] { "/", "Users", "beardtwizzle", "arrayUsage" });

Codifique aquí para otros con un problema similar

public class Path {
    public static String combine(String... paths)
    {
        File file = new File(paths[0]);

        for (int i = 1; i < paths.length ; i++) {
            file = new File(file, paths[i]);
        }

        return file.getPath();
    }
}
isNaN1247
fuente
12

Enfoque independiente de la plataforma (usa File.separator, es decir, el funcionamiento depende del sistema operativo donde se ejecuta el código:

java.nio.file.Paths.get(".", "path", "to", "file.txt")
// relative unix path: ./path/to/file.txt
// relative windows path: .\path\to\filee.txt

java.nio.file.Paths.get("/", "path", "to", "file.txt")
// absolute unix path: /path/to/filee.txt
// windows network drive path: \\path\to\file.txt

java.nio.file.Paths.get("C:", "path", "to", "file.txt")
// absolute windows path: C:\path\to\file.txt
Maksim Kostromin
fuente
11

Para mejorar la respuesta de JodaStephen, Apache Commons IO tiene FilenameUtils que hace esto. Ejemplo (en Linux):

assert org.apache.commons.io.FilenameUtils.concat("/home/bob", "work\\stuff.log") == "/home/bob/work/stuff.log"

Es independiente de la plataforma y producirá los separadores que su sistema necesite.

alpian
fuente
2

Aquí hay una solución que maneja múltiples partes del camino y condiciones de borde:

public static String combinePaths(String ... paths)
{
  if ( paths.length == 0)
  {
    return "";
  }

  File combined = new File(paths[0]);

  int i = 1;
  while ( i < paths.length)
  {
    combined = new File(combined, paths[i]);
    ++i;
  }

  return combined.getPath();
}
Cuenta
fuente
2

Si no necesita más que cadenas, puede usar com.google.common.io.Files

Files.simplifyPath("some/prefix/with//extra///slashes" + "file//name")

Llegar

"some/prefix/with/extra/slashes/file/name"
Gismo Ranas
fuente
1

Tal vez tarde para la fiesta, pero quería compartir mi opinión sobre esto. Estoy usando un patrón Builder y permito appendllamadas convenientemente encadenadas . Se puede ampliar fácilmente para admitir el trabajo con Pathobjetos también.

public class Files  {
    public static class PathBuilder {
        private File file;

        private PathBuilder ( File root ) {
            file = root;
        }

        private PathBuilder ( String root ) {
            file = new File(root);
        }

        public PathBuilder append ( File more ) {
            file = new File(file, more.getPath()) );
            return this;
        }

        public PathBuilder append ( String more ) {
            file = new File(file, more);
            return this;
        }

        public File buildFile () {
            return file;
        }
    }

    public static PathBuilder buildPath ( File root ) {
        return new PathBuilder(root);
    }

    public static PathBuilder buildPath ( String root ) {
        return new PathBuilder(root);
    }
}

Ejemplo de uso:

File root = File.listRoots()[0];
String hello = "hello";
String world = "world";
String filename = "warez.lha"; 

File file = Files.buildPath(root).append(hello).append(world)
              .append(filename).buildFile();
String absolute = file.getAbsolutePath();

El resultado absolutecontendrá algo como:

/hello/world/warez.lha

o tal vez incluso:

A:\hello\world\warez.lha
Stephan Henningsen
fuente
1

Esto también funciona en Java 8:

Path file = Paths.get("Some path");
file = Paths.get(file + "Some other path");
rootExplorr
fuente
0

Esta solución ofrece una interfaz para unir fragmentos de ruta desde una matriz String []. Utiliza java.io.File.File (String parent, String child) :

    public static joinPaths(String[] fragments) {
        String emptyPath = "";
        return buildPath(emptyPath, fragments);
    }

    private static buildPath(String path, String[] fragments) {
        if (path == null || path.isEmpty()) {
            path = "";
        }

        if (fragments == null || fragments.length == 0) {
            return "";
        }

        int pathCurrentSize = path.split("/").length;
        int fragmentsLen = fragments.length;

        if (pathCurrentSize <= fragmentsLen) {
            String newPath = new File(path, fragments[pathCurrentSize - 1]).toString();
            path = buildPath(newPath, fragments);
        }

        return path;
    }

Entonces puedes simplemente hacer:

String[] fragments = {"dir", "anotherDir/", "/filename.txt"};
String path = joinPaths(fragments);

Devoluciones:

"/dir/anotherDir/filename.txt"
JackCid
fuente