Comment combiner des chemins en Java?


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

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

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



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

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

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.


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

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"

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

1

Cela fonctionne également en Java 8:

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

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"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.