Comment combiner des chemins en Java?

350

Existe-t-il un équivalent Java pour System.IO.Path.Combine()C # /. NET? Ou tout code pour accomplir cela?

Cette méthode statique combine une ou plusieurs chaînes dans un chemin.

Rodrigue
la source
1
Cette question SO pourrait aider.
Jim Blizard

Réponses:

407

Plutôt que de tout garder basé sur des chaînes, vous devez utiliser une classe conçue pour représenter un chemin d'accès au système de fichiers.

Si vous utilisez Java 7 ou Java 8, vous devriez fortement envisager d'utiliser java.nio.file.Path; Path.resolvepeut être utilisé pour combiner un chemin avec un autre ou avec une chaîne. La Pathsclasse d'assistance est également utile. Par exemple:

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

Si vous devez gérer des environnements pré-Java-7, vous pouvez utiliser java.io.File, comme ceci:

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

Si vous le souhaitez plus tard sous forme de chaîne, vous pouvez appeler getPath(). En effet, si vous vouliez vraiment imiter Path.Combine, vous pourriez simplement écrire quelque chose comme:

public static String combine(String path1, String path2)
{
    File file1 = new File(path1);
    File file2 = new File(file1, path2);
    return file2.getPath();
}
Jon Skeet
la source
10
Méfiez-vous des chemins absolus. La version .NET retournera path2(en ignorant path1) si path2est un chemin absolu. La version Java supprimera le premier /ou le \ traitera comme un chemin relatif.
finnw
23
@Matthew - car un répertoire est un fichier. C'est un fichier dont le contenu définit les enfants de ce répertoire, leur emplacement sur le disque, les autorisations, etc.
Dónal
7
@Hugo: Cela gaspille donc deux objets entiers ? Choquant! Pour être honnête, cela me semble assez propre ... il conserve la logique des noms de fichiers relatifs où il appartient, dans la classe File.
Jon Skeet
1
@modosansreves: Regardez File.getCanonicalPath.
Jon Skeet
1
@SargeBorsch: Eh bien, C # n'est qu'un langage. Vous pouvez facilement créer votre propre équivalent Fileen C # si vous le souhaitez. (Je suppose que vous voulez dire que l'existence de Fileest un avantage, avec lequel je suis d'accord.)
Jon Skeet
118

Dans Java 7, vous devez utiliser resolve:

Path newPath = path.resolve(childPath);

Bien que la classe NIO2 Path puisse sembler un peu redondante pour File avec une API inutilement différente, elle est en fait subtilement plus élégante et robuste.

Notez que Paths.get()(comme suggéré par quelqu'un d'autre) n'a pas de surcharge en prenant un Path, et faire Paths.get(path.toString(), childPath)n'est PAS la même chose que resolve(). De la Paths.get()documentation :

Notez que bien que cette méthode soit très pratique, son utilisation impliquera une référence supposée au système de fichiers par défaut et limitera l'utilité du code appelant. Par conséquent, il ne doit pas être utilisé dans le code de bibliothèque destiné à une réutilisation flexible. Une alternative plus flexible consiste à utiliser une instance Path existante comme ancre, telle que:

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

La fonction sœur resolveest l'excellente relativize:

Path childPath = path.relativize(newPath);
Aleksandr Dubinsky
la source
42

La principale réponse est d'utiliser des objets File. Cependant Commons IO possède une classe FilenameUtils qui peut faire ce genre de chose, comme la méthode concat () .

JodaStephen
la source
7
FilenameUtils.concat, pour être précis. commons.apache.org/proper/commons-io/apidocs/org/apache/commons/...
MikeFHay
Si vous travaillez avec quelque chose comme JSF, vous voulez vraiment le garder basé sur des chaînes car tous les chemins que vous obtiendrez seront basés sur des chaînes.
DanielK
17

Je sais que c'est longtemps depuis la réponse originale de Jon, mais j'avais une exigence similaire à celle de l'OP.

En étendant la solution de Jon, j'ai proposé ce qui suit, qui prendra un ou plusieurs segments de chemin prend autant de segments de chemin que vous pouvez lui lancer.

Usage

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

Code ici pour les autres personnes ayant un problème similaire

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
la source
12

approche indépendante de la plate-forme (utilise File.separator, c'est-à-dire que will works dépend du système d'exploitation sur lequel le code s'exécute:

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
la source
11

Pour améliorer la réponse de JodaStephen, Apache Commons IO a FilenameUtils qui le fait. Exemple (sous Linux):

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

Il est indépendant de la plate-forme et produira les séparateurs dont votre système a besoin.

alpian
la source
2

Voici une solution qui gère plusieurs parties de chemin et conditions de bord:

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();
}
Facture
la source
2

Si vous n'avez pas besoin de plus de chaînes, vous pouvez utiliser com.google.common.io.Files

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

obtenir

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

Tard dans la soirée peut-être, mais je voulais partager mon point de vue à ce sujet. J'utilise un modèle Builder et autorise les appendappels chaînés de manière pratique . Il peut facilement être étendu pour prendre également en charge le travail avec des Pathobjets.

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

Exemple d'utilisation:

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();

Le résultat absolutecontiendra quelque chose comme:

/hello/world/warez.lha

ou peut-être même:

A:\hello\world\warez.lha
Stephan Henningsen
la source
1

Cela fonctionne également en Java 8:

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

Cette solution offre une interface pour joindre des fragments de chemin à partir d'un tableau String []. Il utilise 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;
    }

Ensuite, vous pouvez simplement faire:

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

Retour:

"/dir/anotherDir/filename.txt"
JackCid
la source